Setup

# library(ggplot2)
# library(reshape2)
library(ggpattern)
library(viridis)
library(colorRamps)
library(gridExtra)
library(ggplot2)
library('igraph')
library(ggnet)
library(network)
library(khroma)
library(dplyr)
library(ggpubr)
library(Biostrings)
Warning: package ‘S4Vectors’ was built under R version 4.2.2Warning: package ‘GenomeInfoDb’ was built under R version 4.2.2
source('similarity.R')
source('seqdotplot.R')
source('msaplot.R')

sunset <- colour("sunset")
discrete_rainbow <- colour("discrete rainbow")

path.base = '../../../'
path.work = paste(path.base, '02_analysis/04_sv/01_data/', sep = '')
path.tair = paste(path.base, '01_data_common/01_tair10/', sep = '')
path.figures = paste(path.base, '02_analysis/04_sv/03_figures/', sep = '')
path.svs = paste(path.base, '01_data_common/02_annot_denovo/02_pannagram/svs/', sep = '')
# path.genes = paste(path.base, '01_data_common/02_annot_denovo/02_pannagram/genes/', sep = '')

# sim.cutoff = 0.9

sim.cutoff = 0.85

Colors


# ---- Colors of TE content ----
te.content.names = c("noTE", "isTE", "hasTE", "hasTEpart", "isTEpart")
te.cols = c('#D8D9CF', '#EB455F', '#7B6079', '#3C8DAD', '#79B773')
names(te.cols) = te.content.names


# ---- Colors of BLAST hits ----
colors.blast.hits <- c("in graph" = "#676FA3",
            "partial overlap" = "#FF9F29",
            "1 self-hit" = "#6EBF8B",
            "0 hits" = "#D82148",
            "in graph but not in SVs" = "#151D3B")


# ---- Colors of known TE families ----
fam.palette = c()
fam.palette['Unassigned'] = 'grey'
fam.palette['Mix'] = 'grey20'
fam.palette['Mix with Helitron'] = '#266D98'
fam.palette['Helitron'] = '#BCACDE'
fam.palette["LTR/Copia"] = '#BFDB38'
fam.palette["LTR/Gypsy"] = '#54B435'
fam.palette["DNA/HAT"] = '#F9B5D0'
fam.palette["DNA+"] = '#C8658C'
fam.palette["DNA"] = '#C8658C'
fam.palette["DNA/MuDR"] = '#971549'


fam.palette["LINE"] = '#FFC26F'
fam.palette["RathE1/2/3_cons"] = '#C38154'
fam.palette["SINE"] = '#884A39'
fam.palette["TEG"] = '#4E3636'


# Colors for new TEs
g.cols = discrete_rainbow(length(unique(sv.memb$prot)))
Error in h(simpleError(msg, call)) : 
  error in evaluating the argument 'x' in selecting a method for function 'unique': object 'sv.memb' not found

TEs


# Load similarity function

bl.file = paste(path.work,'new_te_on_te.fasta',sep = '')
bl.res = read.table(bl.file)
bl.res = bl.res[bl.res$V1 != bl.res$V8,]

bl.res.init = bl.res
bl.res = bl.res[bl.res$V6 >= sim.cutoff * 100,]

res.nest = findNestedness(bl.res, use.strand = F)
[1] 130447
[1] 17626
[1] 3789
[1] 1186
[1] 407
[1] 180
[1] 79
[1] 54
[1] 34
[1] 26
[1] 17
[1] 10
[1] 3
[1] 1
[1] 0
[1] 124919
[1] 17576
[1] 3842
[1] 1240
[1] 437
[1] 189
[1] 89
[1] 61
[1] 41
[1] 28
[1] 21
[1] 11
[1] 6
[1] 2
[1] 0
res.nest.len = sapply(unique(c(res.nest$V1, res.nest$V8)), function(s) as.numeric(strsplit(s, '\\|')[[1]][5]))
  
res.nest$len1 = res.nest.len[res.nest$V1]
res.nest$len8 = res.nest.len[res.nest$V8]
res.nest$p1 = res.nest$C1 / res.nest$len1
res.nest$p8 = res.nest$C8 / res.nest$len8

res.nest.sim = res.nest[(res.nest$p1 >= sim.cutoff) | 
                          (res.nest$p8 >= sim.cutoff),]

How many TEs are in the graph

Distribution among families and subfamilies Distribution among lengths

te.in.graph = unique(c(res.nest$V1, res.nest$V8))

# What is the actual number of TEs
file.content <- readLines(bl.file)

selected.lines <- file.content[grepl("^# Query:|hits found", file.content)]
df.query = data.frame(b.query=selected.lines[seq(1, length(selected.lines), by = 2)],
                      b.hits=selected.lines[seq(2, length(selected.lines), by = 2)])

df.query$query  <- gsub("^# Query: (.*)", "\\1", df.query$b.query)
df.query$len <- as.numeric(sapply(strsplit(df.query$query, "\\|"), function(x) x[5]))
df.query$hits <- as.numeric(stringr::str_extract(df.query$b.hits, "\\d+"))
df.query$val.hits = df.query$hits
df.query$val.hits[df.query$val.hits >= 2] = 2
df.query$val.hits[df.query$query %in% bl.res$V8] = 2
df.query$val.hits[df.query$query %in% te.in.graph] = 3
hit.values = c('0 hits', '1 self-hit', 'partial overlap', 'in graph', "in graph but not in SVs")
df.query$s.hits = hit.values[df.query$val.hits+1]
df.query$s.hits = factor(df.query$s.hits, levels = rev(hit.values))
df.query$family <- sapply(strsplit(df.query$query, "\\|"), function(x) x[9])
df.query$subfam <- sapply(strsplit(df.query$query, "\\|"), function(x) x[8])



# TEs, which are not in SVs
te.in.svs = read.table(paste(path.work, 'blast_tes_on_sv.txt', sep = ''), stringsAsFactors = F)
te.rest = setdiff(df.query$query, te.in.svs$V1)
te.in.svs = read.table(paste(path.work, 'blast_sv_on_tes.txt', sep = ''), stringsAsFactors = F)
te.rest = setdiff(te.rest, te.in.svs$V8)
df.query$s.hits[df.query$query %in% te.rest] = "in graph but not in SVs"


p = ggplot(df.query, aes(x = len, fill = s.hits, color = s.hits)) +
  # geom_histogram(aes(y = ..density..), alpha=0.5, color = "black", bins = 30) +
  # geom_jitter(height = 0.02, width = 0, alpha = 0.7) +
  geom_density(alpha = 0.5) +
  scale_fill_manual(values = colors.blast.hits) +
  scale_color_manual(values = colors.blast.hits) +
   scale_x_log10() +
  labs(fill = NULL, color = NULL) +
  xlab('length of TEs') + ylab('Normalised density') +
  theme_minimal() +
  theme(legend.position = c(1, 1), legend.justification = c(1, 1),
          legend.background = element_rect(color = "grey90"))

p

pdf(paste(path.figures, 'tes_self_blast_len_density.pdf', sep = ''), width = 5, height = 4)
print(p)     # Plot 1 --> in the first page of PDF
dev.off()
quartz_off_screen 
                2 

table(df.query$val.hits)

    0     1     2     3 
 1584 13615   766 19125 

TEs not in SVs

# TEs in te-graph: te.in.graph
# TEs which are have no connection to SVs

df = as.data.frame(table(df.query$s.hits))

p = ggplot(df, aes(x = "", y = Freq, fill = Var1)) +
  geom_bar(stat="identity", width=1, alpha = 0.7) +
  coord_polar("y", start=0) +
  labs(title=NULL, fill="Categories") +
  theme_void()+
    scale_fill_manual(values = colors.blast.hits) +
  geom_text(aes(label = Freq,x = 1.3), position = position_stack(vjust = 0.5)) + theme(legend.position="none")
p

pdf(paste(path.figures, 'tes_self_blast_pie_chart.pdf', sep = ''), width = 3, height = 3)
print(p)     # Plot 1 --> in the first page of PDF
dev.off()
quartz_off_screen 
                2 

Examples

Examples no hits


pdf(paste(path.figures, 'tes_self_scatter_no_hits_long.pdf', sep = ''), width = 5, height = 4)
print(p)     # Plot 1 --> in the first page of PDF
dev.off()
null device 
          1 

head(df.query.tmp[df.query.tmp$family == 'DNA/MuDR',]$query)
[1] "te|641277|641420|1|144|+|AT1TE02080|ARNOLDY1|DNA/MuDR"     
[2] "te|1157763|1157863|1|101|+|AT1TE03780|LIMPET1|DNA/MuDR"    
[3] "te|4546279|4546388|1|110|+|AT1TE14750|ARNOLD2|DNA/MuDR"    
[4] "te|6753991|6754119|1|129|-|AT1TE21830|ATDNAI27T9A|DNA/MuDR"
[5] "te|10655834|10655963|1|130|+|AT1TE34455|ARNOLD1|DNA/MuDR"  
[6] "te|11660991|11661122|1|132|+|AT1TE37760|ATDNA2T9C|DNA/MuDR"

Examples one self-hit

families



df.query.tmp = df.query[(df.query$val.hits == 1),]

cnt.init = c(table(df.query$family))
cnt.tmp = c(table(df.query.tmp$family))

common_names <- intersect(names(cnt.init), names(cnt.tmp))
# Создание dataframe только для совпадающих имен
df_match <- data.frame(names = common_names, values.init = cnt.init[common_names], 
                       values.tmp = cnt.tmp[common_names])


gradient_colors <- c(discrete_rainbow(nrow(df_match)))
names(gradient_colors) = NULL


p = ggplot(df_match, aes(x = values.init, y = values.tmp, label = names, color = names)) +
  geom_point() +
  # geom_text(hjust = 0, vjust = 0) +
  ggrepel::geom_text_repel(max.overlaps = 20) +
  xlab("Initial counts") +
  ylab("Counts in \"1 self-hits\" category") +
  scale_x_log10() +
  scale_y_log10() +
  scale_color_manual(values = gradient_colors) +
  theme(legend.position = "none") +
  guides(color = FALSE) +
  theme_minimal()
p


pdf(paste(path.figures, 'tes_self_scatter_1_selfhits_fam.pdf', sep = ''), width = 5, height = 4)
print(p)     # Plot 1 --> in the first page of PDF
dev.off()
quartz_off_screen 
                2 

subfamilies



df.query.tmp = df.query[(df.query$val.hits == 1) & (df.query$len >= 600),]

cnt.init = c(table(df.query$subfam))
cnt.tmp = c(table(df.query.tmp$subfam))

common_names <- intersect(names(cnt.init), names(cnt.tmp))
# Создание dataframe только для совпадающих имен
df_match <- data.frame(names = common_names, values.init = cnt.init[common_names], 
                       values.tmp = cnt.tmp[common_names])


# gradient_colors <- c(discrete_rainbow(nrow(df_match)))
names(gradient_colors) = NULL


p = ggplot(df_match, aes(x = values.init, y = values.tmp, label = names, color = names)) +
  geom_point() +
  # geom_text(hjust = 0, vjust = 0) +
  ggrepel::geom_text_repel(max.overlaps = 20) +
  xlab("Initial counts") +
  ylab("Counts in \"1 self-hits\" category") +
  scale_x_log10() +
  scale_y_log10() +
  # scale_color_manual(values = gradient_colors) +
  theme(legend.position = "none") +
  guides(color = FALSE) +
  theme_minimal()
p


pdf(paste(path.figures, 'tes_self_scatter_1_selfhits_subfam.pdf', sep = ''), width = 7, height = 5)
print(p)     # Plot 1 --> in the first page of PDF
dev.off()
quartz_off_screen 
                2 

individuals from subfamilies

s.subfam = 'ATREP8'

df.query.tmp = df.query[(df.query$subfam == s.subfam) & (df.query$len >= 600),]
df.query.tmp

Creating the graph

# all edges
idx = res.nest$p1 >= sim.cutoff
edges = cbind(res.nest$V1[idx], res.nest$V8[idx])
idx = res.nest$p8 >= sim.cutoff
edges = rbind(edges, cbind(res.nest$V8[idx], res.nest$V1[idx]))
te.enges.names = unique(c(edges[,1], edges[,2]))
te.enges.fam = sapply(te.enges.names, function(s) strsplit(s, '\\|')[[1]][9] )

te.enges.fam[te.enges.fam %in% c('DNA/Pogo', 'DNA/Tc1', 'DNA/Harbinger', 'DNA/En-Spm',
                     'DNA/HAT', 'DNA', 'DNA/Mariner')] = 'DNA'
te.enges.fam[te.enges.fam %in% c('RathE1_cons', 'RathE2_cons', 'RathE3_cons')] = 'RathE1/2/3_cons'
te.enges.fam[te.enges.fam %in% c('LINE/L1', 'LINE?')] = 'LINE'
te.enges.fam[te.enges.fam %in% c('Unassigned')] = 'Mix'
te.enges.fam[te.enges.fam %in% c('RC/Helitron')] = 'Helitron'

edges = edges[te.enges.fam[edges[,1]] != 'TEG',]
edges = edges[te.enges.fam[edges[,2]] != 'TEG',]
te.enges.names = unique(c(edges[,1], edges[,2]))


# nodes
idx = (res.nest$p1 >= sim.cutoff) & (res.nest$p8 >= sim.cutoff)
te.nodes = cbind(res.nest$V1[idx], res.nest$V8[idx])
te.nodes = te.nodes[te.enges.fam[te.nodes[,1]] != 'TEG',]
te.nodes = te.nodes[te.enges.fam[te.nodes[,2]] != 'TEG',]

te.rest = setdiff(te.enges.names, c(te.nodes[,1], te.nodes[,2]))


te.nodes.graph <- igraph::make_graph(t(te.nodes), directed = T)
te.nodes.graph <- igraph::simplify(te.nodes.graph)
te.nodes.comp <- igraph::components(te.nodes.graph)

nodes = data.frame(node = paste('N', te.nodes.comp$membership, sep = ''), 
                   te = names(te.nodes.comp$membership))

nodes.rest = data.frame(node = paste('R', (1:length(te.rest)), sep = ''), te = te.rest)
nodes = rbind(nodes, nodes.rest)

rownames(nodes) = nodes$te


nodes.cnt = data.frame(cnt = c(table(nodes$node)))
nodes.cnt$node = rownames(nodes.cnt)
nodes.cnt$fam = sapply(nodes.cnt$node, function(s){
  s.te = nodes$te[nodes$node == s]
  fam.te = unique(te.enges.fam[s.te])
  if(length(fam.te) == 1){
    return(fam.te)
  } else {
    fam.te = setdiff(fam.te, 'TEG')
    if(length(fam.te) == 1) return(fam.te)
    return('Mix')
  }
})
table(nodes.cnt$fam)

            DNA        DNA/MuDR        Helitron            LINE       LTR/Copia       LTR/Gypsy             Mix 
           1109            1228            2302             356             503            1837              67 
RathE1/2/3_cons            SINE 
             53              27 
# Redefine edges but with node names
idx.endes = (edges[,1] %in% nodes$te) & (edges[,2] %in% nodes$te)
b.graph = cbind(nodes[edges[idx.endes,1], 'node'],nodes[edges[idx.endes,2], 'node'])
b.graph = unique(b.graph)
# b.graph = b.graph[b.graph[,1] != b.graph[,2],]
b.graph.uni = b.graph[b.graph[,1] == b.graph[,2],]
b.graph = b.graph[b.graph[,1] != b.graph[,2],]

length(unique(c(b.graph[,1], b.graph[,2])))
[1] 7245
# reduce indirect arrows
idx.remove = c()
for(i.edge in 1:nrow(b.graph)){
  if(i.edge %% 1000 == 0) print(i.edge)
  tmp.to = b.graph[b.graph[,1] == b.graph[i.edge,1],2]
  tmp.from = b.graph[b.graph[,2] == b.graph[i.edge,2],1]
  if(length(intersect(tmp.to, tmp.from)) > 0) idx.remove = c(idx.remove, i.edge)
}
[1] 1000
[1] 2000
[1] 3000
[1] 4000
[1] 5000
[1] 6000
[1] 7000
[1] 8000
[1] 9000
[1] 10000
[1] 11000
[1] 12000
[1] 13000
[1] 14000
[1] 15000
[1] 16000
[1] 17000
[1] 18000
[1] 19000
[1] 20000
idx.remove = unique(idx.remove)
b.graph = b.graph[-idx.remove,]
# b.graph = rbind(b.graph, b.graph.uni)

# Print graph

g.nodes.fam = nodes.cnt$fam
names(g.nodes.fam) = nodes.cnt$node
g.nodes.cnt = nodes.cnt$cnt
names(g.nodes.cnt) = nodes.cnt$node

g.cols = discrete_rainbow(length(unique(g.nodes.fam)))
names(g.cols) = unique(g.nodes.fam)

b.graph.init = b.graph


g.part <- network(b.graph, matrix.type = "edgelist", ignore.eval = FALSE, directed = TRUE)
b.graph.names = network.vertex.names(g.part)

Old colors

p <- ggnet2(g.part, label = F, edge.color = "black", 
            node.size = g.nodes.cnt[b.graph.names], 
            color = g.nodes.fam[b.graph.names],
            palette = g.cols,
            # mode = "kamadakawai"
            ) 
Loading required package: sna
Loading required package: statnet.common

Attaching package: ‘statnet.common’

The following objects are masked from ‘package:base’:

    attr, order

sna: Tools for Social Network Analysis
Version 2.7-1 created on 2023-01-24.
copyright (c) 2005, Carter T. Butts, University of California-Irvine
 For citation information, type citation("sna").
 Type help(package="sna") to get started.


Attaching package: ‘sna’

The following objects are masked from ‘package:igraph’:

    betweenness, bonpow, closeness, components, degree, dyad.census, evcent, hierarchy, is.connected,
    neighborhood, triad.census

Loading required package: scales

Attaching package: ‘scales’

The following object is masked from ‘package:viridis’:

    viridis_pal

Warning: 'length(x) = 6909 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 6909 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 6909 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 6909 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 6909 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 6909 > 1' in coercion to 'logical(1)'
p + guides(size = F)


# 
# b.graph.fam = cbind(g.nodes.fam[b.graph[,1]], g.nodes.fam[b.graph[,2]])
# b.graph.fam
# 
# which((b.graph.fam[,1] == 'DNA/MuDR') & (b.graph.fam[,1] == 'LINE'))

New Family colors

# g.fam.names = sort(unique(g.nodes.fam))
# fam.palette = c()
# idx.pallete = c()
# 
# idx.fam <- grep("^Helitron", g.fam.names, value = FALSE)
# tmp.palette <- colorRampPalette(c('#BFACE2', '#266D98', '#422B72'))(length(idx.fam))
# idx.pallete = c(idx.pallete, idx.fam)
# fam.palette = c(fam.palette, tmp.palette)
# 
# idx.fam <- grep("^LTR", g.fam.names, value = FALSE)
# tmp.palette <- colorRampPalette(c('#BFDB38', '#54B435'))(length(idx.fam))
# idx.pallete = c(idx.pallete, idx.fam)
# fam.palette = c(fam.palette, tmp.palette)
# 
# idx.fam <- grep("^DNA", g.fam.names, value = FALSE)
# tmp.palette <- colorRampPalette(c('#F9B5D0', '#971549'))(length(idx.fam))
# idx.pallete = c(idx.pallete, idx.fam)
# fam.palette = c(fam.palette, tmp.palette)
# 
# idx.fam = setdiff(1:length(g.fam.names), idx.pallete)
# tmp.palette <- colorRampPalette(c('#FFC26F', '#C38154', '#884A39', '#4E3636'))(length(idx.fam))
# idx.pallete = c(idx.pallete, idx.fam)
# fam.palette = c(fam.palette, tmp.palette)
# 
# names(fam.palette) = g.fam.names[idx.pallete]
# fam.palette['Unassigned'] = 'grey'
# fam.palette['Mix'] = 'black'
# fam.palette['TEG'] = 'darkgreen'

Separately visualise connected components

tmp.graph <- igraph::make_graph(t(b.graph), directed = T)
tmp.graph <- igraph::simplify(tmp.graph)
tmp.comp <- igraph::components(tmp.graph)

tmp.cnt = table(tmp.comp$membership)
tmp.cnt = -sort(-tmp.cnt)
head(tmp.cnt)

   2   51  209  176  104   29 
3493   40   38   37   32   31 
k = 1
tmp.k = as.numeric(names(tmp.cnt)[k])
tmp.names = names(tmp.comp$membership)[tmp.comp$membership == tmp.k]
b.graph.sub = b.graph[(b.graph[,1] %in% tmp.names) & 
                        (b.graph[,2] %in% tmp.names),]

g.part.sub.big <- network(b.graph.sub, matrix.type = "edgelist", ignore.eval = FALSE, directed = TRUE)
b.graph.names.sub.big = network.vertex.names(g.part.sub.big)


set.seed(20)
p <- ggnet2(g.part.sub.big, label = F, edge.color = "black", 
            node.size = g.nodes.cnt[b.graph.names.sub.big], 
            color = g.nodes.fam[b.graph.names.sub.big],
            mode = 'kamadakawai',
            palette = fam.palette) + guides(size = F)
Warning: 'length(x) = 3493 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3493 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3493 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3493 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3493 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3493 > 1' in coercion to 'logical(1)'
p.big.type = p + theme(legend.position = "none")

# set.seed(20)
# p <- ggnet2(g.part.sub.big, label = F, edge.color = "black", 
#             node.size = g.nodes.cnt[b.graph.names.sub.big], 
#             color = g.nodes.fam[b.graph.names.sub.big],
#             mode = 'kamadakawai',
#             palette = fam.palette) + guides(size = F)
# p.big.color = p + theme(legend.position = "none")


tmp.k = as.numeric(names(tmp.cnt)[k])
tmp.names = names(tmp.comp$membership)[tmp.comp$membership != tmp.k]
b.graph.sub = b.graph[(b.graph[,1] %in% tmp.names) & 
                        (b.graph[,2] %in% tmp.names),]

g.part.sub.small <- network(b.graph.sub, matrix.type = "edgelist", ignore.eval = FALSE, directed = TRUE)
b.graph.names.sub.small = network.vertex.names(g.part.sub.small)


set.seed(20)
p <- ggnet2(g.part.sub.small, label = F, edge.color = "black", 
            node.size = g.nodes.cnt[b.graph.names.sub.small], 
            color = g.nodes.fam[b.graph.names.sub.small],
            # mode = 'kamadakawai',
            palette = fam.palette) + guides(size = F)
Warning: 'length(x) = 3416 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3416 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3416 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3416 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3416 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3416 > 1' in coercion to 'logical(1)'
p.small.type =p + theme(legend.position = "none")

# set.seed(20)
# p <- ggnet2(g.part.sub.small, label = F, edge.color = "black", 
#             node.size = g.nodes.cnt[b.graph.names.sub.small], 
#             color = g.nodes.fam[b.graph.names.sub.small],
#             # mode = 'kamadakawai',
#             palette = fam.palette) + guides(size = F)
# p.small.color = p + theme(legend.position = "none")

Plots

p.big.type

p.small.type


pdf(paste(path.figures, 'graph_tes_family_small.pdf', sep = ''), width = 9, height = 9)
print(p.small.type)     # Plot 1 --> in the first page of PDF
dev.off()
quartz_off_screen 
                2 
pdf(paste(path.figures, 'graph_tes_family_big.pdf', sep = ''), width = 5, height = 5)
print(p.big.type)     # Plot 1 --> in the first page of PDF
dev.off()
quartz_off_screen 
                2 

Stop for the paper

stop()

Specific TE families

Graph of one family

sort(-table(df.query$subfam[(df.query$val.hits == 3) & (df.query$family == 'LTR/Copia')]))

     META1  ATCOPIA95  ATCOPIA57  ATCOPIA28  ATCOPIA41  ATCOPIA49  ROMANIAT5  ATCOPIA94  ATCOPIA13  ATCOPIA37 
       -58        -51        -34        -33        -28        -28        -28        -27        -23        -22 
 ATCOPIA65  ATCOPIA43  ATCOPIA35  ATCOPIA42  ATCOPIA69  ATCOPIA78  ATCOPIA27  ATCOPIA58      ATRE1  ATCOPIA29 
       -22        -21        -16        -15        -15        -15        -14        -14        -14        -13 
 ATCOPIA54  ATCOPIA45  ATCOPIA51  ATCOPIA66  ATCOPIA75  ATCOPIA12  ATCOPIA36  ATCOPIA50  ATCOPIA67   ENDOVIR1 
       -12        -11        -11        -11        -11        -10        -10        -10        -10        -10 
 ATCOPIA16  ATCOPIA21  ATCOPIA22  ATCOPIA34   ATCOPIA4  ATCOPIA44  ATCOPIA48  ATCOPIA62  ATCOPIA63  ATCOPIA64 
        -9         -9         -9         -9         -9         -9         -9         -9         -9         -9 
 ATCOPIA87  ATCOPIA11  ATCOPIA55  ATCOPIA61  ATCOPIA70  ATCOPIA15  ATCOPIA25  ATCOPIA30  ATCOPIA52  ATCOPIA93 
        -9         -8         -8         -8         -8         -7         -7         -7         -7         -7 
 ATCOPIA96  ATCOPIA26  ATCOPIA31  ATCOPIA56  ATCOPIA8A   ATCOPIA9   ATCOPIA1  ATCOPIA10  ATCOPIA23   ATCOPIA3 
        -7         -6         -6         -6         -6         -6         -5         -5         -5         -5 
ATCOPIA31A  ATCOPIA38  ATCOPIA40   ATCOPIA5  ATCOPIA83  ATCOPIA89  ATCOPIA91  ATCOPIA97  ATCOPIA14  ATCOPIA17 
        -5         -5         -5         -5         -5         -5         -5         -5         -4         -4 
  ATCOPIA2  ATCOPIA24  ATCOPIA32  ATCOPIA33 ATCOPIA38B  ATCOPIA39  ATCOPIA46  ATCOPIA68  ATCOPIA74  ATCOPIA88 
        -4         -4         -4         -4         -4         -4         -4         -4         -4         -4 
 ATCOPIA8B  ATCOPIA90  ATCOPIA19  ATCOPIA60  ATCOPIA72  ATCOPIA76  ATCOPIA77  ATCOPIA79  ATCOPIA82  ATCOPIA85 
        -4         -4         -3         -3         -3         -3         -3         -3         -3         -3 
 ATCOPIA86      TA1-2  ATCOPIA18 ATCOPIA18A  ATCOPIA20 ATCOPIA32B  ATCOPIA47  ATCOPIA53  ATCOPIA59 ATCOPIA65A 
        -3         -3         -2         -2         -2         -2         -2         -2         -2         -2 
 ATCOPIA71  ATCOPIA73  ATCOPIA81  ATCOPIA92 ATCOPIA38A   ATCOPIA6   ATCOPIA7  ATCOPIA80  ATCOPIA84 
        -2         -2         -2         -2         -1         -1         -1         -1         -1 

# one.te.fam = 'BRODYAGA1'
# one.te.fam = 'BRODYAGA2'
# one.te.fam = 'HELITRONY1D'
# one.te.fam = 'HELITRONY3'
one.te.fam = 'ATCOPIA41'
query.fam = df.query$query[df.query$subfam == one.te.fam]


one.te.fam = 'ATCOPIA41'
query.fam = df.query$query[df.query$subfam == one.te.fam]

res.nest.famp = res.nest[(res.nest$V1 %in% query.fam) | (res.nest$V8 %in% query.fam),]


idx = res.nest.famp$p1 >= sim.cutoff
edges = cbind(res.nest.famp$V1[idx], res.nest.famp$V8[idx])
idx = res.nest.famp$p8 >= sim.cutoff
edges = rbind(edges, cbind(res.nest.famp$V8[idx], res.nest.famp$V1[idx]))


te.enges.names = unique(c(edges[,1], edges[,2]))
te.enges.fam = sapply(te.enges.names, function(s) strsplit(s, '\\|')[[1]][9] )
te.enges.fam[te.enges.fam %in% c('DNA/Pogo', 'DNA/Tc1', 'DNA/Harbinger', 'DNA/En-Spm',
                     'DNA/HAT', 'DNA', 'DNA/Mariner')] = 'DNA'
te.enges.fam[te.enges.fam %in% c('RathE1_cons', 'RathE2_cons', 'RathE3_cons')] = 'RathE1/2/3_cons'
te.enges.fam[te.enges.fam %in% c('LINE/L1', 'LINE?')] = 'LINE'
te.enges.fam[te.enges.fam %in% c('Unassigned')] = 'Mix'
te.enges.fam[te.enges.fam %in% c('RC/Helitron')] = 'Helitron'

g.part <- network(edges, matrix.type = "edgelist", ignore.eval = FALSE, directed = TRUE)
b.graph.names = network.vertex.names(g.part)
b.graph.len = as.numeric(sapply(strsplit(b.graph.names, "\\|"), function(x) x[5]))


label.family = sapply(strsplit(b.graph.names, "\\|"), function(x) x[8])
lab.cols = c('#3F2E3E', "white")
label.color = lab.cols[(label.family == one.te.fam) + 1]

set.seed(20)
p <- ggnet2(g.part, label = b.graph.len, edge.color = "black", 
             node.size = 15,
            alpha=0.8,
            arrow.gap = 0.015,
            arrow.size = 5,
            label.color = label.color,
            # node.size = g.nodes.cnt[b.graph.names], 
            color = te.enges.fam[b.graph.names],
            palette = fam.palette,
            # mode = "kamadakawai"
            ) + guides(size = F)
p 

pdf(paste(path.figures, 'real_tes_subfam_', one.te.fam, '.pdf', sep = ''), width = 20, height = 18)
print(p)     # Plot 1 --> in the first page of PDF
dev.off()

set.seed(20)
p <- ggnet2(g.part, label = b.graph.names, edge.color = "black",
             node.size = 15,
            alpha=0.8,
            arrow.gap = 0.015,
            arrow.size = 5,
            # label.color = label.color,
            # node.size = g.nodes.cnt[b.graph.names],
            color = te.enges.fam[b.graph.names],
            palette = fam.palette,
            # mode = "kamadakawai"
            ) + guides(size = F)

pdf(paste(path.figures, 'real_tes_subfam_', one.te.fam, '_names.pdf', sep = ''), width = 50, height = 49)
print(p)     # Plot 1 --> in the first page of PDF
dev.off()

Dotplots

Read TE sequences

file.te.fasta = '/Volumes/Samsung_T5/vienn/tair/new_filtration/new_te.fasta'
te.fasta = seqinr::read.fasta(file.te.fasta)
Warning: cannot open file '/Volumes/Samsung_T5/vienn/tair/new_filtration/new_te.fasta': No such file or directoryError in file(con, "r") : cannot open the connection

One pairwise example

one VS all


wsize = 10
nmatch = 8

name0 = 'te|11683565|11689821|3|6257|+|AT3TE48540|ATCOPIA95|LTR/Copia'
name0 = 'te|16691748|16695154|1|3407|−|AT1TE55070|ATCOPIA41|LTR/Copia'
name0 = gsub('−', "-", name0)


one.te.fam = strsplit(name0, '\\|')[[1]][8]
# one.te.fam = 'BRODYAGA2'
query.fam = df.query$query[df.query$subfam == one.te.fam]
query.fam = query.fam[(query.fam %in% res.nest.sim$V1) | (query.fam %in% res.nest.sim$V2)]

names.all = setdiff(query.fam, name0)

p.all = list()
for(name2 in names.all){
  # message(name2)
  seq1 = te.fasta[[name0]]
  seq2 = te.fasta[[name2]]
  
  s1 = strsplit(name0, '\\|')[[1]][7]
  s2 = strsplit(name2, '\\|')[[1]][7]
  p = dotplot(seq1, seq2, wsize, nmatch) + xlab(s1) + ylab(s2)
  p.all[[name2]] = p
}
# 
# pp = grid.arrange(grobs = p.all, ncol = 13) ## display plot
# 
# 
# pdf(paste(path.figures, 'pairwise_all','.pdf', sep = ''), width = 50, height = 50)
# print(pp)     # Plot 1 --> in the first page of PDF
# dev.off()

s0 = paste0(strsplit(name0, '\\|')[[1]][7:9], collapse = '_')
s0 = gsub("/", "-", s0)
pdf(paste(path.figures, 'pairwise_all_',s0,'.pdf', sep = ''), width = 50, height = 50)
grid.arrange(grobs = p.all, ncol = ceiling(sqrt(length(p.all)))) # Write the grid.arrange in the file
dev.off() # Close the file

one connected component


wsize = 10
nmatch = 8


name0 = 'te|6205621|6206184|2|564|−|AT2TE25255|HELITRONY1D|RC/Helitron'
name0 = 'te|14189256|14190266|5|1011|-|AT5TE50700|HELITRONY3|RC/Helitron'
name0 = 'te|12513239|12513824|1|586|+|AT1TE40725|ATHILA4A|LTR/Gypsy'
name0 = 'te|11647426|11648912|1|1487|+|AT1TE37705|ATREP7|RC/Helitron'
name0 = 'te|11683565|11689821|3|6257|+|AT3TE48540|ATCOPIA95|LTR/Copia'
name0 = gsub('−', "-", name0)


names.all = unique(c(res.nest.sim$V1[res.nest.sim$V8 == name0],
                     res.nest.sim$V8[res.nest.sim$V1 == name0]))
# names.all = unique(c(res.nest$V1[res.nest$V8 == name0], 
#                      res.nest$V8[res.nest$V1 == name0]))

p.all = list()
for(name2 in names.all){
  # message(name2)
  seq1 = te.fasta[[name0]]
  seq2 = te.fasta[[name2]]
  
  s1 = paste0(strsplit(name0, '\\|')[[1]][7:9], collapse = '|')
  s2 = paste0(strsplit(name2, '\\|')[[1]][7:9], collapse = '|')
  p = dotplot(seq1, seq2, wsize, nmatch) + xlab(s1) + ylab(s2)
  p.all[[name2]] = p
}


s0 = paste0(strsplit(name0, '\\|')[[1]][7:9], collapse = '_')
s0 = gsub("/", "-", s0)
pdf(paste(path.figures, 'pairwise_connect_',s0,'.pdf', sep = ''), width = 50, height = 50)
grid.arrange(grobs = p.all, ncol = ceiling(sqrt(length(p.all)))) # Write the grid.arrange in the file
dev.off() # Close the file


name1 = 'te|14189256|14190266|5|1011|-|AT5TE50700|HELITRONY3|RC/Helitron'
name2 = 'te|2162295|2162937|2|643|-|AT2TE09950|HELITRONY3|RC/Helitron'
name0 = name1

names.all = unique(c(res.nest$V1[res.nest$V8 == name0], res.nest$V8[res.nest$V1 == name0]))


names = c(name1, name2)
b.tmp = bl.res[(bl.res$V1 %in% names) & (bl.res$V8 %in% names),]

res.nest[(res.nest$V1 %in% names) & (res.nest$V8 %in% names), ]

SVs

Readings seSVs


sv.se = readRDS(paste(path.svs, 'sv_se.rds', sep = ''))

# Rename length groups
lev.replace = c('[1,10]', '(10,15]')
lev.new = '[1,15]'

s.levels = as.character(levels(sv.se$len.gr))
s.levels = s.levels[!(s.levels %in% lev.replace)]
s.levels = c(lev.new, s.levels)
s.levels = gsub("e\\+03", "k", s.levels)

sv.se$len.gr = as.character(sv.se$len.gr)
sv.se$len.gr[sv.se$len.gr %in% lev.replace] = lev.new
sv.se$len.gr = gsub("e\\+03", "k", sv.se$len.gr)
sv.se$len.gr = factor(sv.se$len.gr, levels = s.levels)


# Replace families
sv.se$fam = as.character(sv.se$fam)
sv.se$fam <- gsub("Helitron/.*", "Mix with Helitron", sv.se$fam)


sv.se$te = factor(sv.se$te, levels = c('isTE', 'isTEpart', 'hasTE', 'hasTEpart', 'noTE'))

Read SV fasta


sv.seqs = seqinr::read.fasta(paste(path.svs, 'sv_pangen_seq_sv_big.fasta', sep = ''))

Reading nestedness


# Load similarity function

file.nestedness = paste(path.work, 'sv_big_on_big_nest.rds', sep = '')


if(!file.exists(file.nestedness)){
  bl.file = paste(path.work, 'sv_big_on_big.txt', sep = '')
  bl.res = read.table(bl.file)
  bl.res = bl.res[bl.res$V1 != bl.res$V8,]
  
  bl.res = bl.res[bl.res$V6 >= sim.cutoff * 100,]

  res.nest = findNestedness(bl.res, use.strand = F)
    
  res.nest$len1 = res.nest.len[res.nest$V1]
  res.nest$len8 = res.nest.len[res.nest$V8]
  res.nest$p1 = res.nest$C1 / res.nest$len1
  res.nest$p8 = res.nest$C8 / res.nest$len8  
  saveRDS(res.nest, file.nestedness, compress = F)
} else {
  res.nest = readRDS(file.nestedness)
}

res.nest.len = sapply(unique(c(res.nest$V1, res.nest$V8)), 
                      function(s) as.numeric(strsplit(s, '\\|')[[1]][2]))
res.nest0 = res.nest


# For further analysis of examples:
bl.file = paste(path.work, 'sv_big_on_big.txt', sep = '')
bl.res = read.table(bl.file)
bl.res = bl.res[bl.res$V1 != bl.res$V8,]

TE stat

In graph - not in graph

res.nest = res.nest0

sv.se.len = sv.se[sv.se$len >= 100,]
sv.se.len$in.connect = sv.se.len$name %in% names(res.nest.len)

cnt.sv.se = table(sv.se.len$in.connect , sv.se.len$te)
cnt.sv.se
       
        isTE isTEpart hasTE hasTEpart noTE
  FALSE   80      749   254       538 5748
  TRUE  4260     2560  2830      1384 1916
df = reshape2::melt(cnt.sv.se)



df$Var2 = factor(df$Var2, levels = rev(c('isTE', 'isTEpart', 'hasTE', 'hasTEpart', 'noTE')))


# install.packages("ggpattern")


p = ggplot(df, aes(x = Var2, y = value, fill = Var2, alpha = Var1, color = Var1)) +
  geom_col_pattern( aes(pattern = Var1),
    # pattern = rep(c('none', "stripe"), 5),
    pattern_density = 0.1,
    pattern_spacing = 0.025,
    pattern_fill = "grey70", 
    position = "dodge", 
    width = 0.8
  ) + 
  # geom_col(position = "dodge", width = 0.8) +
  scale_alpha_manual(values = c(0.8, 1), labels = c("No", "Yes")) +
  scale_color_manual(values = c('black', 'black'), labels = c("not in graph", "in graph")) +
  scale_pattern_manual(values = c("stripe", 'none'), labels = c("in graph", "not in graph"),
                       breaks = c(TRUE, FALSE)) +
  labs(fill = "", pattern='Connected to others') +
  scale_fill_manual(values = te.cols) +
  xlab(NULL) +
  ylab("Number of SVs") +
  theme(axis.text.y = element_blank()) + 
  guides(alpha = "none", fill = 'none', color = 'none') +
  theme_minimal() + coord_flip() +
  theme(
    legend.position = c(0.7, 0.3),     # Adjust these coordinates as needed
    legend.background = element_rect(fill="transparent", color='grey70')  
  ) +
  theme(axis.text.y = element_blank()) +
  guides(pattern = guide_legend(override.aes = list(fill = c("white"), color= 'black')))  
p

pdf(paste(path.figures, 'graph_mob_in_graph.pdf', sep = ''), width = 3, height = 4)
print(p)     # Plot 1 --> in the first page of PDF
dev.off()
quartz_off_screen 
                2 

TE families in SV types


sv.se.len = sv.se[sv.se$len >= 100,]
cnt.fam.sv = table(sv.se.len$fam[sv.se.len$fam!=''], sv.se.len$te[sv.se.len$fam!=''])
cnt.fam.sv = t(apply(cnt.fam.sv, 1, function(x) x/sum(x)))
cnt.fam.sv = cnt.fam.sv[, colSums(cnt.fam.sv) != 0]
cnt.fam.sv = reshape2::melt(cnt.fam.sv)

p = ggplot(cnt.fam.sv, aes(x = Var2, y = Var1, color = Var2)) + 
  geom_point(aes(size = value, alpha = value * 2)) + theme_minimal() + 
  scale_color_manual(values = te.cols)  +
  geom_text(data = cnt.fam.sv[cnt.fam.sv$value >= 0.2,], 
              aes(x=Var2, y=Var1, label = round(value, 2)), 
              size = 2.5, color = 'black', 
            nudge_x = 0.3,
            nudge_y = 0) +
  guides(size = "none", alpha = "none", color = 'none') +
  xlab('SV type') + ylab('TE family')
p



cnt.fam.sv = rowSums(table(sv.se.len$fam[sv.se.len$fam!=''], sv.se.len$te[sv.se.len$fam!='']))
cnt.fam.sv = data.frame(value = cnt.fam.sv, names = names(cnt.fam.sv))
rownames(cnt.fam.sv) = NULL

g = ggplot(cnt.fam.sv, aes(x = names, y = value)) +
  geom_bar(stat="identity", fill="grey80")+
  coord_flip() + theme_minimal() + theme(axis.title.y = element_blank(),
                        axis.text.y = element_blank(),
                        axis.ticks.y = element_blank()) +
  scale_y_continuous(labels = paste("1e",seq(0,4,1), sep = ''), breaks= seq(0,4,1)*1000) +
  ylab('#') + geom_text(aes(label=value, y=0), hjust=0, size = 2.5)
g 



pp = ggpubr::ggarrange(p + xlab('TE content') + scale_x_discrete(labels = c('is compl.', 'is fragm.', 
                               'cont. compl.', 'cont. fragm.')) , g, ncol = 2, widths = c(0.75, 0.25))
pp

pdf(paste(path.figures, 'graph_mob_te_fam_sv_type.pdf', sep = ''), width = 6, height = 4)
print(pp)     # Plot 1 --> in the first page of PDF
dev.off()
quartz_off_screen 
                2 

# Insertion and deletion
idx = (sv.se.len$fam!='') & (sv.se.len$freq.max <= 3)
table(sv.se.len$fam[idx], sv.se.len$te[idx])
                   
                    isTE isTEpart hasTE hasTEpart noTE
  DNA/HAT            123       20    91        35    0
  DNA/MuDR           529      195   494       114    0
  DNA+               323       78   263        57    0
  Helitron          1192      651   395       354    0
  LINE                66      162   257       129    0
  LTR/Copia          753      180   216       201    0
  LTR/Gypsy          121      170   150        62    0
  Mix                  0       16   152        87    0
  Mix with Helitron  104       22   172        68    0
  RathE1/2/3_cons      8       12     8         9    0
  SINE                 1        0     3         2    0
  TEG                 72       95    23        72    0
  Unassigned          12       20    21        33    0
idx = (sv.se.len$fam!='') & (sv.se.len$freq.max >= 25) & (sv.se.len$len >= 100) 
table(sv.se.len$fam[idx], sv.se.len$te[idx])
                   
                    isTE isTEpart hasTE hasTEpart noTE
  DNA/HAT              7        5     5         3    0
  DNA/MuDR            10      111    31        20    0
  DNA+                10       55    25        13    0
  Helitron            18      241    64       111    0
  LINE                 8       39    14        20    0
  LTR/Copia            5       49    16         7    0
  LTR/Gypsy            2      121    22        13    0
  Mix                  0       14     7        10    0
  Mix with Helitron    0       13     7        13    0
  RathE1/2/3_cons      1        1     7         2    0
  SINE                 1        1     2         3    0
  TEG                  2       36     2        12    0
  Unassigned           1        6     0         1    0

TE fam: TAIR10

f.te.ref = paste(path.tair, 'new_te.fasta', sep = '')
lines = readLines(f.te.ref)
lines = grep('^>', lines, value = T)

ref.fam = sapply(lines, function(x) strsplit(x, '\\|')[[1]][9])


indices <- grep("^DNA(?!/HAT|/MuDR)", ref.fam, value = FALSE, perl = TRUE)
ref.fam[indices] = 'DNA+'

indices <- grep("^RathE", ref.fam, value = FALSE, perl = TRUE)
ref.fam[indices] = 'RathE1/2/3_cons'

indices <- grep("^LINE", ref.fam, value = FALSE, perl = TRUE)
ref.fam[indices] = 'LINE'
ref.fam[ref.fam == 'RC/Helitron'] = 'Helitron'

ref.fam.cnt = table(ref.fam)



df = cnt.fam.sv
df$ref = as.numeric(ref.fam.cnt[df$names])
df = df[!is.na(df$ref),]

plot(df$value, df$ref)




p <- ggplot(df, aes(x = ref, y = value, color = names)) +
  geom_smooth(aes(group = 1), method = "lm", formula = y ~ x, se = FALSE, color = 'grey70') + 
  geom_point() +
  ggrepel::geom_text_repel(aes(label = names), max.overlaps = 20) +
  # xlab("log # in TAIR10 annotation") +
  # ylab("log # in SVs") +
  # scale_x_log10() +
  # scale_y_log10() +
  xlab("# in TAIR10 annotation") +
  ylab("# in SVs") +
  theme(legend.position = "none") +
  guides(color = FALSE) +
  theme_minimal()
Warning: The `<scale>` argument of `guides()` cannot be `FALSE`. Use "none" instead as of ggplot2 3.3.4.
p


lm_model <- lm(value ~ ref, data = df)
slope <- coef(lm_model)[2]


p = p + annotate("text", x = min(df$ref), y = max(df$value), 
           label = paste('Slope:', round(slope, 3)), hjust = 0, vjust = 1)



pdf(paste(path.figures, 'graph_mob_te_fam_tair10.pdf', sep = ''), width = 4, height = 4)
print(p)     # Plot 1 --> in the first page of PDF
dev.off()
quartz_off_screen 
                2 

Graph

Filtration


res.nest = res.nest0

sv.names.mix = sv.se$name[grep("^Mix", sv.se$fam)]
res.nest = res.nest[!(res.nest$V1 %in% sv.names.mix),]
res.nest = res.nest[!(res.nest$V8 %in% sv.names.mix),]


sv.names.mix = sv.se$name[sv.se$te == 'noTE']
res.nest = res.nest[!(res.nest$V1 %in% sv.names.mix),]
res.nest = res.nest[!(res.nest$V8 %in% sv.names.mix),]

singleton.mode = F
if(singleton.mode){
  sv.names.freq = sv.se$name[sv.se$freq.max <= 3]
  # sv.names.freq = sv.se$name[sv.se$freq.max >= 25]
  res.nest = res.nest[res.nest$V1 %in% sv.names.freq,]
  res.nest = res.nest[res.nest$V8 %in% sv.names.freq,]
}

prefix.mode = c('', '_single')

Construct

# all edges
idx = res.nest$p1 >= sim.cutoff
edges = cbind(res.nest$V1[idx], res.nest$V8[idx])
idx = res.nest$p8 >= sim.cutoff
edges = rbind(edges, cbind(res.nest$V8[idx], res.nest$V1[idx]))
te.enges.names = unique(c(edges[,1], edges[,2]))

tmp = sv.se$te
names(tmp) = sv.se$name
te.enges.type = as.character(tmp[te.enges.names])
names(te.enges.type) <- names(tmp[te.enges.names])


tmp = sv.se$fam
names(tmp) = sv.se$name
te.enges.fam = tmp[te.enges.names]

# nodes
idx = (res.nest$p1 >= sim.cutoff) & (res.nest$p8 >= sim.cutoff)
te.nodes = cbind(res.nest$V1[idx], res.nest$V8[idx])
te.rest = setdiff(te.enges.names, c(te.nodes[,1], te.nodes[,2]))


te.nodes.graph <- igraph::make_graph(t(te.nodes), directed = T)
te.nodes.graph <- igraph::simplify(te.nodes.graph)
te.nodes.comp <- igraph::components(te.nodes.graph)

nodes = data.frame(node = paste('N', te.nodes.comp$membership, sep = ''), te = names(te.nodes.comp$membership))

nodes.rest = data.frame(node = paste('R', (1:length(te.rest)), sep = ''), te = te.rest)
nodes = rbind(nodes, nodes.rest)

rownames(nodes) = nodes$te

# Define TE type
nodes.cnt = data.frame(cnt = c(table(nodes$node)))
nodes.cnt$node = rownames(nodes.cnt)
nodes.cnt$type = sapply(nodes.cnt$node, function(s){
  s.te = nodes$te[nodes$node == s]
  type.te = unique(te.enges.type[s.te])
  if(length(type.te) == 1){
    return(type.te)
  } else {
    type.te = table(type.te)
    type.te = names(type.te)[type.te == max(type.te)]
    return(type.te[1])
  }
})
table(nodes.cnt$type)

    hasTE hasTEpart      isTE  isTEpart 
     1027       447       484      1846 
# Define TE family
nodes.cnt$fam = sapply(nodes.cnt$node, function(s){
  s.te = nodes$te[nodes$node == s]
  type.te = unique(te.enges.fam[s.te])
  if(length(type.te) == 1){
    return(type.te)
  } else {
    type.te = table(type.te)
    type.te = names(type.te)[type.te == max(type.te)]
    return(type.te[1])
  }
})
table(nodes.cnt$fam)

        DNA/HAT        DNA/MuDR            DNA+        Helitron            LINE       LTR/Copia       LTR/Gypsy 
            123             688             359             807             444             456             684 
RathE1/2/3_cons            SINE             TEG      Unassigned 
             28              13             151              51 
# Redefine edges but with node names
idx.endes = (edges[,1] %in% nodes$te) & (edges[,2] %in% nodes$te)
b.graph = cbind(nodes[edges[idx.endes,1], 'node'],nodes[edges[idx.endes,2], 'node'])
b.graph = unique(b.graph)
# b.graph = b.graph[b.graph[,1] != b.graph[,2],]
b.graph.uni = b.graph[b.graph[,1] == b.graph[,2],]
b.graph = b.graph[b.graph[,1] != b.graph[,2],]

length(unique(c(b.graph[,1], b.graph[,2])))
[1] 3567
# reduce indirect arrows
idx.remove = c()
for(i.edge in 1:nrow(b.graph)){
  if(i.edge %% 1000 == 0) print(i.edge)
  tmp.to = b.graph[b.graph[,1] == b.graph[i.edge,1],2]
  tmp.from = b.graph[b.graph[,2] == b.graph[i.edge,2],1]
  if(length(intersect(tmp.to, tmp.from)) > 0) idx.remove = c(idx.remove, i.edge)
}
[1] 1000
[1] 2000
[1] 3000
[1] 4000
[1] 5000
[1] 6000
[1] 7000
[1] 8000
idx.remove = unique(idx.remove)
b.graph = b.graph[-idx.remove,]
# b.graph = rbind(b.graph, b.graph.uni)

# Print graph

g.nodes.type = nodes.cnt$type
names(g.nodes.type) = nodes.cnt$node
g.nodes.cnt = nodes.cnt$cnt
names(g.nodes.cnt) = nodes.cnt$node
g.nodes.fam = nodes.cnt$fam
names(g.nodes.fam) = nodes.cnt$node


# g.cols.names = c("noTE", "isTE", "hasTE", "hasTEpart", "isTEpart")
# g.cols = c('#FFD966', '#EB455F', '#7B6079', '#3C8DAD', '#79B773')
# names(g.cols) = g.cols.names


g.part <- network(b.graph, matrix.type = "edgelist", ignore.eval = FALSE, directed = TRUE)
b.graph.names = network.vertex.names(g.part)

set.seed(20)
p <- ggnet2(g.part, label = F, edge.color = "black", 
            node.size = g.nodes.cnt[b.graph.names], 
            color = g.nodes.type[b.graph.names],
            # mode = 'kamadakawai',
            # arrow.gap = 0, 
            # arrow.size = 3,
            palette = te.cols) + guides(size = F)
Loading required package: sna
Loading required package: statnet.common

Attaching package: ‘statnet.common’

The following object is masked from ‘package:Biostrings’:

    order

The following object is masked from ‘package:XVector’:

    order

The following object is masked from ‘package:IRanges’:

    order

The following object is masked from ‘package:S4Vectors’:

    order

The following object is masked from ‘package:BiocGenerics’:

    order

The following objects are masked from ‘package:base’:

    attr, order

sna: Tools for Social Network Analysis
Version 2.7-1 created on 2023-01-24.
copyright (c) 2005, Carter T. Butts, University of California-Irvine
 For citation information, type citation("sna").
 Type help(package="sna") to get started.


Attaching package: ‘sna’

The following objects are masked from ‘package:igraph’:

    betweenness, bonpow, closeness, components, degree, dyad.census, evcent, hierarchy, is.connected,
    neighborhood, triad.census

Loading required package: scales

Attaching package: ‘scales’

The following object is masked from ‘package:viridis’:

    viridis_pal

Warning: 'length(x) = 3565 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3565 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3565 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3565 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3565 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3565 > 1' in coercion to 'logical(1)'Warning: The `<scale>` argument of `guides()` cannot be `FALSE`. Use "none" instead as of ggplot2 3.3.4.
p

# path.figures  = '/Volumes/Samsung_T5/vienn/work_te/'
pdf(paste(path.figures, 'graph_mob_all_cluster', prefix.mode[singleton.mode+1] ,'_type.pdf', sep = ''), 
    width = 4.6, height = 4.6)
print(p+ theme(legend.position = "none"))     # Plot 1 --> in the first page of PDF
dev.off()
quartz_off_screen 
                2 

# set.seed(20)
# p <- ggnet2(g.part, label = F, edge.color = "grey30", 
#             node.size = g.nodes.cnt[b.graph.names], 
#             color = c('TE', 'noTE')[(g.nodes.type[b.graph.names] == 'noTE')*1+1],
#             # mode = 'kamadakawai',
#             # arrow.gap = 0, 
#             # arrow.size = 3,
#             palette = c('noTE' = 'black', 'TE' = '#AEC3AE')) + guides(size = F)
# p
# 
# # path.figures  = '/Volumes/Samsung_T5/vienn/work_te/'
# pdf(paste(path.figures, 'graph_mob_all_cluster', prefix.mode[singleton.mode+1] ,'_type.pdf', sep = ''), 
#     width = 5, height = 5)
# print(p+ theme(legend.position = "none"))     # Plot 1 --> in the first page of PDF
# dev.off()

Colored by TE family


if(length(setdiff(g.nodes.fam, names(fam.palette)))!=0) stop('not all families are defined')

set.seed(20)
p <- ggnet2(g.part, label = F, edge.color = "grey20", 
            node.size = g.nodes.cnt[b.graph.names], 
            color = g.nodes.fam[b.graph.names],
            # mode = 'kamadakawai',
            # arrow.gap = 0, 
            # arrow.size = 3,
            palette = fam.palette) + guides(size = F)
Warning: 'length(x) = 3565 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3565 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3565 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3565 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3565 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3565 > 1' in coercion to 'logical(1)'
p = p + theme(legend.text = element_text(size = 8), 
          legend.title = element_blank(),
          legend.key.size = unit(0.5, "cm")) + guides(color = guide_legend(ncol = 2))
p


pdf(paste(path.figures, 'graph_mob_all_cluster', prefix.mode[singleton.mode+1] ,'_family.pdf', sep = ''), 
    width = 4.6, height = 4.6)
print(p+ theme(legend.position = "none"))     # Plot 1 --> in the first page of PDF
dev.off()
quartz_off_screen 
                2 
pdf(paste(path.figures, 'graph_mob_all_cluster', prefix.mode[singleton.mode+1] ,'_family_legend.pdf', sep = ''), width = 7, height = 5)
print(p+ coord_fixed(ratio = 1))     # Plot 1 --> in the first page of PDF
dev.off()
quartz_off_screen 
                2 

Node size distribution

df = data.frame(node = unique(nodes$node))
df$size = g.nodes.cnt[df$node]
df$fam = g.nodes.fam[df$node]
df$type = g.nodes.type[df$node]

fam.palette
       Unassigned               Mix Mix with Helitron          Helitron         LTR/Copia         LTR/Gypsy 
           "grey"          "grey20"         "#266D98"         "#BCACDE"         "#BFDB38"         "#54B435" 
          DNA/HAT              DNA+               DNA          DNA/MuDR              LINE   RathE1/2/3_cons 
        "#F9B5D0"         "#C8658C"         "#C8658C"         "#971549"         "#FFC26F"         "#C38154" 
             SINE               TEG 
        "#884A39"         "#4E3636" 
p = ggplot(df, aes(x = type, y = size, color=fam)) +
  geom_jitter(width = 0.2) +
  labs(x = "Type", y = "Size") + 
  scale_y_continuous(trans = "log2") +
  scale_color_manual(values = fam.palette)+
  theme_minimal() +
  guides(color = guide_legend(ncol = 2)) +
  labs(color = "TE family") + xlab('') + ylab('Node size (Number of similar SVs)')
p


pdf(paste(path.figures, 'graph_mob_size_distribution.pdf', sep = ''), width = 6.5, height = 4)
print(p)     # Plot 1 --> in the first page of PDF
dev.off()
quartz_off_screen 
                2 

Separately visualise connected components

tmp.graph <- igraph::make_graph(t(b.graph), directed = T)
tmp.graph <- igraph::simplify(tmp.graph)
tmp.comp <- igraph::components(tmp.graph)

tmp.cnt = table(tmp.comp$membership)
tmp.cnt = -sort(-tmp.cnt)
head(tmp.cnt)

   1   39    4   26   34   33 
1574   41   36   28   24   23 

Save

# p.big.type
# p.big.color
# p.small.type
# p.small.color

size.units = 4.6

pdf(paste(path.figures, 'graph_mob_big_cluster', prefix.mode[singleton.mode+1] ,'_type.pdf', sep = ''), 
    width = size.units, height = size.units)
print(p.big.type)     # Plot 1 --> in the first page of PDF
dev.off()
null device 
          1 
pdf(paste(path.figures, 'graph_mob_big_cluster', prefix.mode[singleton.mode+1] ,'_family.pdf', sep = ''), 
    width = size.units, height = size.units)
print(p.big.color)     # Plot 1 --> in the first page of PDF
dev.off()
null device 
          1 
pdf(paste(path.figures, 'graph_mob_small_cluster', prefix.mode[singleton.mode+1] ,'_type.pdf', sep = ''), 
    width = 6, height = 6)
print(p.small.type)     # Plot 1 --> in the first page of PDF
dev.off()
null device 
          1 
pdf(paste(path.figures, 'graph_mob_small_cluster', prefix.mode[singleton.mode+1] ,'_family.pdf', sep = ''), 
    width = 6, height = 6)
print(p.small.color)     # Plot 1 --> in the first page of PDF
dev.off()
null device 
          1 

Examples with big “has TE”


path.components = paste(path.figures, 'components/', sep = '')
if (!dir.exists(path.components)) {
  dir.create(path.components)
}

# Find a big node, whish is "Have te", which which is not in the biggest connected component

nodes.havete.big = nodes.cnt$node[(nodes.cnt$cnt >= 7) & ((nodes.cnt$type == 'hasTE') | (nodes.cnt$type == 'hasTEpart'))]
nodes.havete.big = nodes.havete.big[nodes.havete.big %in% names(tmp.comp$membership)]


nodes.target = unique(nodes.havete.big[tmp.comp$membership[nodes.havete.big] != as.numeric(names(tmp.cnt)[1])])

# for(i.target in 1:min(80, length(nodes.target))){
for(i.target in c(1)){
  message(i.target)
  
  comp.target = as.numeric(tmp.comp$membership[nodes.target[i.target]])
  
  # Visualise one component
  
  tmp.k = comp.target
  tmp.names = names(tmp.comp$membership)[tmp.comp$membership == tmp.k]
  b.graph.sub = b.graph[(b.graph[,1] %in% tmp.names) | 
                          (b.graph[,2] %in% tmp.names),,drop=F]
  
  g.part.sub.big <- network(b.graph.sub, matrix.type = "edgelist", ignore.eval = FALSE, directed = TRUE)
  b.graph.names.sub.big = network.vertex.names(g.part.sub.big)
  
  
  set.seed(20)
  p.te <- ggnet2(g.part.sub.big, label = F, edge.color = "black", 
              node.size = g.nodes.cnt[b.graph.names.sub.big], 
              color = g.nodes.type[b.graph.names.sub.big],
              mode = 'kamadakawai',
              arrow.gap = 0.04, arrow.size = 5,
              palette = te.cols) + guides(size = F)+ theme(legend.position = "none")
  
  
  set.seed(20)
  p.fam <- ggnet2(g.part.sub.big, label = g.nodes.cnt[b.graph.names.sub.big], edge.color = "black", 
              node.size = g.nodes.cnt[b.graph.names.sub.big],
              # node.size = 10, 
              color = g.nodes.fam[b.graph.names.sub.big],
              mode = 'kamadakawai',
              arrow.gap = 0.04, arrow.size = 5,
              palette = fam.palette) + guides(size = F)+ theme(legend.position = "none")
  
  pp = ggarrange(p.te, p.fam, nrow=1)
  
  # pp
  
  # TODO: third plot - frequency plot (three of colors: insertion, deletion, indel)
  
  pdf(paste(path.components, 'graph_mob_component_', sprintf("%03d", i.target),'.pdf', sep = ''), 
      width = 6, height = 3)
  print(pp)     # Plot 1 --> in the first page of PDF
  dev.off()

}
1
Warning: 'length(x) = 5 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 5 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 5 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 5 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 5 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 5 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 5 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 5 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 5 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 5 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 5 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 5 > 1' in coercion to 'logical(1)'

Get sequences and align


path.mafft = paste(path.work, 'mafft/', sep = '')
if (!dir.exists(path.mafft)) {
  dir.create(path.mafft)
}
  
sv.name.target = nodes$te[nodes$node %in% tmp.names]

seqs.target = sv.seqs[sv.name.target]
seqs.target.len = as.numeric(sapply(names(seqs.target), function(s) strsplit(s, '\\|')[[1]][2]))
seqs.target = seqs.target[order(-seqs.target.len)]
sv.name.target = names(seqs.target)

# Find orientations of sequences


bl.target = bl.res[(bl.res$V1 %in% sv.name.target) & (bl.res$V8 %in% sv.name.target),]
orientation.target = rep('.', length(seqs.target))
names(orientation.target) = sv.name.target
orientation.target[1] = '+'

# TODO: define the first orientation by the longest ORF

dir.seq = c('-', '+')

for(i in 1:(length(orientation.target) - 1)){
  bl.tmp = bl.target[bl.target$V1 == sv.name.target[i],]
  bl.tmp = bl.tmp[order(-bl.tmp$V7),]
  bl.tmp = bl.tmp[!duplicated(bl.tmp$V8),]
  bl.tmp$dir = bl.tmp$V5 > bl.tmp$V4
  bl.tmp = bl.tmp[orientation.target[bl.tmp$V8] == '.',]
  if(nrow(bl.tmp) == 0) next
  
  if(orientation.target[i] == '+'){
    orientation.target[bl.tmp$V8] = dir.seq[1 + (bl.tmp$dir) * 1]
  }
  
  if(orientation.target[i] == '-'){
    orientation.target[bl.tmp$V8] = dir.seq[2 - (bl.tmp$dir) * 1]
  }
  
  if(sum(orientation.target == '.') == 0) break
}
# 
for(i in which(orientation.target == '-')){
  seqs.target[[i]] = rev(seqinr::comp(seqs.target[[i]]))
}

seqs.target.msa = unlist(lapply(seqs.target, function(s) paste0(s, collapse = '')))
seqs.target.msa <- DNAStringSet(seqs.target.msa)

# alignment <- msa(seqs.target.msa)



# Run the alignment
tmp.fasta = paste(path.mafft, 'tmp.fasta', sep = '')
aln.fasta = paste(path.mafft, 'aln.fasta', sep = '')
writeXStringSet(seqs.target.msa, filepath = tmp.fasta)

system(paste('mafft --op 5 --quiet --maxiterate 100 ', tmp.fasta, '>', aln.fasta,  sep = ' '))

alignment = readDNAStringSet(aln.fasta)

seqs.mx = as.matrix(alignment)
msaplot(seqs.mx)

Alignment step-by step

# Align all sequences within each node

# sv.name.target = nodes$te[nodes$node %in% tmp.names]

aln.nodes = list()
seqs.all.cons = c()
for(s.node in tmp.names){
  sv.name.node = nodes$te[nodes$node %in% s.node]
  seqs.name.node = seqs.target.msa[sv.name.node]
  
  if(length(sv.name.node) == 1){
    seqs.mx = as.matrix(seqs.name.node)
    aln.nodes[[s.node]] = seqs.mx
    seqs.all.cons[s.node] = as.character(seqs.name.node)
    next
  }
  
  tmp.fasta = paste(path.mafft, 'tmp.fasta', sep = '')
  aln.fasta = paste(path.mafft, 'aln.fasta', sep = '')
  writeXStringSet(seqs.name.node, filepath = tmp.fasta)
  
  system(paste('mafft --op 5 --quiet --maxiterate 100 ', tmp.fasta, '>', aln.fasta,  sep = ' '))
  
  alignment = readDNAStringSet(aln.fasta)[sv.name.node]
  seqs.mx = as.matrix(alignment)
  # msaplot(seqs.mx)
  
  # add consensus sequence

  cons.prof = seqinr::consensus(seqs.mx, method = 'profile')
  cons.prof = cons.prof[!(rownames(cons.prof) == '-'),]
  max.indexes <- apply(cons.prof, 2, which.max)
  cons.seq = rownames(cons.prof)[max.indexes]
  # seqs.mx = rbind(seqs.mx, cons.seq)
  
  aln.nodes[[s.node]] = seqs.mx
  seqs.all.cons[s.node] = paste0(cons.seq, collapse = '')
}

# Combine all seqs for the alignment
 
# Align consensuses
seqs.all.cons <- DNAStringSet(seqs.all.cons)

# Run the alignment
tmp.fasta = paste(path.mafft, 'tmp.fasta', sep = '')
aln.fasta = paste(path.mafft, 'aln.fasta', sep = '')
writeXStringSet(seqs.all.cons, filepath = tmp.fasta)

system(paste('mafft --op 5 --quiet --maxiterate 100 ', tmp.fasta, '>', aln.fasta,  sep = ' '))

alignment = readDNAStringSet(aln.fasta)

seqs.mx.cons = as.matrix(alignment)
msaplot(seqs.mx.cons)


# Combine consensuses back
seqs.mx.complete = c()
for(s.node in rownames(seqs.mx.cons)){
  aln.tmp = aln.nodes[[s.node]]
  mx.tmp = matrix('-', nrow = nrow(aln.tmp), ncol = ncol(seqs.mx.cons), dimnames = list(rownames(aln.tmp), NULL))
  mx.tmp[, seqs.mx.cons[s.node,] != '-'] = aln.tmp
  seqs.mx.complete = rbind(seqs.mx.complete, mx.tmp)
}

p.msa = msaplot(seqs.mx.complete[sv.name.target,])
# p.msa = msaplot(seqs.mx.complete)
p.msa


pdf(paste(path.components, 'graph_mob_component_', sprintf("%03d", i.target),'_msa.pdf', sep = ''), 
    width = 6, height = 4)
print(p.msa)     # Plot 1 --> in the first page of PDF
dev.off()
quartz_off_screen 
                2 

Create graph from without node collapse

s = rownames(seqs.mx)

s1 = as.character(as.vector(seqs.target.msa[[1]]))
s2 = as.character(as.vector(seqs.target.msa[[4]]))
dotplot(s1, s2, 10, 9)

s1 = as.character(as.vector(seqs.target.msa[['SVgr_1_id_122936|638']]))
s2 = as.character(as.vector(seqs.target.msa[["SVgr_5_id_136083|674"]]))
dotplot(s1, s2, 10, 8)


bl.target[(bl.target$V1 %in% s.tmp) & (bl.target$V8 %in% s.tmp), ]

s1 = as.character(as.vector(seqs.target.msa[[1]]))
s2 = as.character(as.vector(seqs.target.msa[[4]]))
dotplot(s1, s2, 10, 8)


s2 = as.character(seqs.target.msa[[1]])
# todo: %ORF

Stop

stop('All graphs are created, Stop before the code for \"per accession\" ')

Run by accessions

path.figures.acc = '/Volumes/Samsung_T5/vienn/work_te/figures_tegraph_accessions/'
sv.bin = read.table('/Volumes/Samsung_T5/vienn/work_sv/svs_se_bin_v03.txt', stringsAsFactors = F, check.names = FALSE)
# acc = '10002'

for(acc in colnames(sv.bin)){
  sv.acc = rownames(sv.bin)[sv.bin[,acc] == 1]
  rownames(sv.se) = sv.se$gr
  sv.acc = sv.se[sv.acc, 'name']
  
  sv.acc = intersect(sv.acc, rownames(nodes))
  nodes.cnt.acc = table(nodes[sv.acc,'node'])
  
  
  sv.alpha = rep(0, length(b.graph.names))
  names(sv.alpha) = b.graph.names
  sv.alpha[names(sv.alpha) %in% names(nodes.cnt.acc)] = 1
  
  # set.seed(239)
  # p <- ggnet2(g.part, label = F, edge.color = "black", 
  #             node.size = g.nodes.cnt[b.graph.names], 
  #             color = g.nodes.fam[b.graph.names],
  #             alpha = sv.alpha,
  #             # mode = 'kamadakawai',
  #             # arrow.gap = 0, 
  #             # arrow.size = 3,
  #             palette = fam.palette) + guides(size = F) + theme(legend.position = "none")
  
  set.seed(20)
  p <- ggnet2(g.part.sub.small, label = F, edge.color = "black", 
            node.size = g.nodes.cnt[b.graph.names.sub.small], 
            color = g.nodes.fam[b.graph.names.sub.small],
            alpha = sv.alpha[b.graph.names.sub.small],
            # mode = 'kamadakawai',
            palette = fam.palette) + guides(size = F) + theme(legend.position = "none")

  pdf(paste(path.figures.acc, 'graph_te', prefix.mode[singleton.mode+1] ,'_small_acc_',acc,'.pdf', sep = ''), width = 5, height = 5)
  print(p)     # Plot 1 --> in the first page of PDF
  dev.off()
  
  
  set.seed(20)
  p <- ggnet2(g.part.sub.big, label = F, edge.color = "black", 
            node.size = g.nodes.cnt[b.graph.names.sub.big], 
            color = g.nodes.fam[b.graph.names.sub.big],
            alpha = sv.alpha[b.graph.names.sub.big],
            mode = 'kamadakawai',
            palette = fam.palette) + guides(size = F) + theme(legend.position = "none")

  pdf(paste(path.figures.acc, 'graph_te', prefix.mode[singleton.mode+1] ,'_big_acc_',acc,'.pdf', sep = ''), width = 5, height = 5)
  print(p)     # Plot 1 --> in the first page of PDF
  dev.off()

}

p 
sv.annot = read.table('/Volumes/Samsung_T5/vienn/work_sv/svs_annotation_v03.txt', stringsAsFactors = F)
rownames(sv.annot) = sv.annot$gr
head(sv.annot)

sv.annot[extracted_values,]

Big TE-nodes

n.amount = 20

g.part <- network(b.graph, matrix.type = "edgelist", ignore.eval = FALSE, directed = TRUE)
b.graph.names = network.vertex.names(g.part)

size.big = g.nodes.cnt[b.graph.names]
alpha.big = rep(1, length(b.graph.names))
names(alpha.big) = b.graph.names
alpha.big[size.big < n.amount] = 0

sum(size.big >= n.amount)

set.seed(20)
p <- ggnet2(g.part, label = F, edge.color = "black", 
            node.size = size.big, 
            color = g.nodes.fam[b.graph.names],
            alpha= alpha.big,
            # mode = 'kamadakawai',
            # arrow.gap = 0, 
            # arrow.size = 3,
            palette = fam.palette) + guides(size = F) + guides(color = guide_legend(ncol = 2))
p

pdf(paste(path.figures, 'graph_small_cluster', prefix.mode[singleton.mode+1] ,'_family_amount.pdf', sep = ''), width = 5, height = 5)
print(p+ theme(legend.position = "none"))     # Plot 1 --> in the first page of PDF
dev.off()

Which families specifically, and is the rate of insertion is different?

compare number of insertions with the total number of TE load


big.families = data.frame(node =  names(size.big)[size.big >= n.amount])
big.families$size = size.big[big.families$node]
big.families$fam = g.nodes.fam[big.families$node]
big.families = big.families[order(-big.families$size),]
rownames(big.families) = NULL

node.big = nodes[nodes$node %in% big.families$node,]

v = read.table(paste(path.work, 'blast_sv_on_tes.txt', sep = ''))
v = v[v$V1 %in% node.big$te,]


pos.len1 = 2
pos.len2 = 5
v1.len = sapply(unique(v$V1), function(s) as.numeric(strsplit(s,'\\|')[[1]][pos.len1]))
v8.len = sapply(unique(v$V8), function(s) as.numeric(strsplit(s,'\\|')[[1]][pos.len2]))
v.len = c(v1.len, v8.len)

v.sim = findNestedness(v, use.strand = F)

v.sim = findNestedness(v, use.strand = F)
v.sim$p1 = v.sim$C1 / v.len[v.sim$V1]
v.sim$p8 = v.sim$C8 / v.len[v.sim$V8]
v.sim$p1.in8 = v.sim$C1 / v.len[v.sim$V8]
v.sim$p8.in1 = v.sim$C8 / v.len[v.sim$V1]

node.big$subfam = ''
for(sv.name in unique(v.sim$V1)){
  v.tmp = v.sim[v.sim$V1 == sv.name,]
  s = v.tmp$V8[which.max(v.tmp$p1)]
  s = strsplit(s, '\\|')[[1]][8]
  node.big[sv.name, 'subfam'] = s
}


x = tapply(node.big$subfam, node.big$node, function(x){
  cnt = table(x)
  x = names(cnt)[cnt == max(cnt)]
  return(paste0(x, collapse =  ','))
})

big.families$subfam = x[big.families$node]

no-TE SV

Construct

sv.se = readRDS(paste(path.svs, 'sv_se.rds', sep = ''))
sim.cutoff = 0.85


sv.se.no.te = sv.se$name[(sv.se$te == 'noTE') & (sv.se$len > 50)]

bl.file = paste(path.work,'sv_big_on_big.txt', sep = '')
bl.sv = read.table(bl.file, stringsAsFactors = F)
bl.sv = bl.sv[bl.sv$V1 != bl.sv$V8,]

# remove having TEs
bl.sv = bl.sv[bl.sv$V1 %in% sv.se.no.te, ]
bl.sv = bl.sv[bl.sv$V8 %in% sv.se.no.te, ]

pos.len1 = 2
sv.len = sapply(unique(c(bl.sv$V1, bl.sv$V8)), function(s) as.numeric(strsplit(s,'\\|')[[1]][pos.len1]))
bl.sv$len1 = sv.len[bl.sv$V1]
bl.sv$len8 = sv.len[bl.sv$V8]
max.len = 20000
bl.sv = bl.sv[(bl.sv$len1 <= max.len) & (bl.sv$len8 <= max.len),]
bl.sv$p1 = (bl.sv$V3 - bl.sv$V2 + 1) / bl.sv$len1
bl.sv$p8 = (abs(bl.sv$V5 - bl.sv$V4) + 1) / bl.sv$len8
bl.sv$comb = as.factor(paste(bl.sv$V1, bl.sv$V8, sep = '||'))

idx.mutual = (bl.sv$p1 >= sim.cutoff) & (bl.sv$p8 >= sim.cutoff)
# There is a big discussion in my head, whether it should be '&' or '|'
# If it's not ,utual, then maybe with something else it will construct a mutual relation, 
# so we should remain for the analysis of nestedness all partial inclusions
sv.mutual = bl.sv[idx.mutual, ]
v = bl.sv[!idx.mutual, ]
v = v[!(v$comb %in% sv.mutual$comb),]

# At some point it was a step to remain only those instances which are not "unique" in combinations
# but I think it's not correct here

sv.sim = findNestedness(v, use.strand = T)
sv.sim$p1 = sv.sim$C1 / sv.len[sv.sim$V1]
sv.sim$p8 = sv.sim$C8 / sv.len[sv.sim$V8]

# here  we should finally use '|', not '&'
sv.nested = sv.sim[(sv.sim$p1 >= sim.cutoff) | (sv.sim$p8 >= sim.cutoff) ,]

# Create pre-data for defining edges
common.names = intersect(colnames(sv.mutual), colnames(sv.nested))
sv.overall = rbind(sv.mutual[,common.names], sv.nested[,common.names])
sv.overall$group = (sv.overall$p1 >= sim.cutoff) * 1 + (sv.overall$p8 >= sim.cutoff) * 2
idx1 = sv.overall$group != 2  # V1 in V8
idx2 = sv.overall$group != 1  # V8 in V1


# Edges 
sv.edges = rbind(cbind(sv.overall$V1[idx1], sv.overall$V8[idx1]),
                 cbind(sv.overall$V8[idx2], sv.overall$V1[idx2]))


sv.graph <- igraph::make_graph(t(sv.edges), directed = T)
sv.graph <- igraph::simplify(sv.graph)
sv.graphcomp <- igraph::components(sv.graph)

sv.memb = data.frame(memb = sv.graphcomp$membership)
sv.memb$name = rownames(sv.memb)
rownames(sv.memb) = NULL
rownames(sv.se) = sv.se$name
sv.memb$te = sv.se[sv.memb$name, 'te']
sv.memb$cover = sv.se[sv.memb$name, 'cover'] / sv.se[sv.memb$name, 'len']
sv.memb$len = sv.len[sv.memb$name]

Plot all

g.part <- network(sv.edges, matrix.type = "edgelist", ignore.eval = FALSE, directed = TRUE)
b.graph.names = network.vertex.names(g.part)

set.seed(239)
p <- ggnet2(g.part, label = F, edge.color = "black", 
            node.size = 1,
            # node.size = g.nodes.cnt[b.graph.names], 
            # color = g.nodes.type[b.graph.names],
            # palette = te.cols
            ) + guides(size = F)
p 

Plot with colors

sv.prot.init = readRDS(paste(path.work, 'sv_proteins_no_te_blast.rds', sep = ''))
sv.prot.init$name = sapply(sv.prot.init$X1, function(s){
  s = paste0(strsplit(s, '\\|')[[1]][1:2], collapse = '|')
  return(s)
})
sv.prot = sv.prot.init[sv.prot.init$prot == 1,]
sv.prot[,2] = tolower(sv.prot[,2])

types = c('disease', 'repeat', 'receptor',  'zinc', 'transcriptase', 'reverse', 'transpos')
for(i.type in 1:length(types)){
  sv.prot[,types[i.type]] = (grepl(types[i.type], sv.prot[,2])) * 1
}
sv.prot$type = rowSums(sv.prot[,types])
table(sv.prot$type)



sv.memb$prot = 'no prot'
sv.memb$prot[sv.memb$name %in% sv.prot.init$name] = 'undefined prot'
sv.memb$prot[sv.memb$name %in% sv.prot$name] = 'defined prot'
for(type in types){
  sv.memb$prot[sv.memb$name %in% sv.prot$name[sv.prot[,type] == 1]] = type
}

g.nodes.prot = sv.memb$prot
g.nodes.prot[g.nodes.prot == 'disease'] = 'defined prot'
names(g.nodes.prot) = sv.memb$name



set.seed(239)
p <- ggnet2(g.part, label = F, edge.color = "black", 
            # node.size = g.nodes.cnt[b.graph.names], 
            node.size = 1,
            color = g.nodes.prot[b.graph.names],
            palette = g.cols,
            # mode = "kamadakawai"
            ) + guides(size = F) + coord_fixed(ratio = 1) +
  scale_color_manual(values = g.cols, 
                       breaks = c("transpos","reverse",
                                  "repeat","zinc","receptor", "defined prot", "undefined prot",
                                  "no prot"), 
                     name = 'Protein key-word:') + theme(legend.justification = c(1, 0))
p = p+ theme(legend.key.height = unit(0.5, "cm"))
p

pdf(paste(path.figures, 'graph_new_all.pdf', sep = ''), width = 6, height = 4)
print(p)     # Plot 1 --> in the first page of PDF
dev.off()

# 
# cnt = table(g.nodes.prot)
# cnt = c(sum(cnt[c("transpos","reverse","repeat","zinc")]), sum(cnt[c("receptor","defined prot")]),
#         cnt["undefined prot"], cnt["no prot"])

Types of the component


sv.graph <- igraph::make_graph(t(sv.edges), directed = T)
sv.graph <- igraph::simplify(sv.graph)
sv.graphcomp <- igraph::components(sv.graph)

sv.comp.member = sv.graphcomp$membership

s.tags = c("transpos","reverse","repeat","zinc", "receptor","defined prot", "undefined prot", 'no prot')
s.tags0 = rep('', length(s.tags))
s.tags0[1:4] = 'TE-like'
s.tags0[5:6] = 'Known Proteins'
s.tags0[7] = 'Undef. Proteins'
s.tags0[8] = 'No Proteins'
names(s.tags0) = s.tags

comp.tags = rep('', length(unique(sv.comp.member)))
for(s.tag in s.tags){
  tmp.tags = unique(sv.comp.member[names(g.nodes.prot)[g.nodes.prot == s.tag]])
  comp.tags[tmp.tags][comp.tags[tmp.tags] == ''] = s.tag
}
comp.tags[comp.tags == ''] = 'no prot'
comp.tags = data.frame(table(comp.tags))
colnames(comp.tags) = c('tag1', 'freq')
comp.tags$tag1 = factor(comp.tags$tag1, levels = s.tags)
comp.tags = comp.tags[order(comp.tags$tag1),]

comp.tags$tag0 = s.tags0[comp.tags$tag1]
comp.tags$tag0 = factor(comp.tags$tag0, levels = unique(s.tags0))

y.ticks = tapply(comp.tags$freq, comp.tags$tag0, sum)
y.ticks = y.ticks[!is.na(y.ticks)]

yy = sum(y.ticks) - cumsum(y.ticks) + y.ticks/2

comp.tags$ymin <- c(0, cumsum(comp.tags$freq)[-length(comp.tags$freq)])
comp.tags$ymax <- cumsum(comp.tags$freq)

x.step = rep(0, 8)
n.step = 10
x.step[c(5,7,8)] = n.step
x.step = cumsum(x.step)

comp.tags$ymin = comp.tags$ymin + x.step
comp.tags$ymax = comp.tags$ymax + x.step

y.min = tapply(comp.tags$ymin, comp.tags$tag0, min)
y.max = tapply(comp.tags$ymax, comp.tags$tag0, max)
y.val = (y.max + y.min) / 2
y.cnt = tapply(comp.tags$freq, comp.tags$tag0, sum)

df.text = data.frame(y.min = y.min, y.max = y.max, y.val = y.val, y.cnt = y.cnt, label = names(y.val))
df.text$angles <- 360 - (df.text$y.val / (max(comp.tags$ymax) + n.step)) * 360 
df.text$angles[2:3] = 180 + df.text$angles[2:3]

p = ggplot(comp.tags, aes(x = 0, y = freq, fill = tag1)) +
   geom_rect(aes(xmin = -0.5, xmax = 0.5, ymin = ymin, ymax = ymax)) +
   coord_polar("y", start = 0) +
   scale_fill_manual(values = g.cols.plus) + ylim(0, max(comp.tags$ymax) + n.step) +
   theme_void() + xlim(-1.5, 0.7) + 
   geom_text(data=df.text, aes(x = 0.7, y = y.val, label = paste(label, y.cnt, sep = ': ')), 
             angle = df.text$angles, inherit.aes = FALSE) +
  theme(legend.position="none") + 
  annotate("text", x = -1.5, y = 0, label = paste('Total',sum(comp.tags$freq),'\n connected \ncomponents')) 

p

pdf(paste(path.figures, 'graph_new_pie_chart.pdf', sep = ''), width = 3.1, height = 3.1)
print(p)     # Plot 1 --> in the first page of PDF
dev.off()

I don’t know

sv.se$freq = sv.se$freq.max
n.cutoff = 3
n = 28
sv.se$sin = 'indel'
sv.se$sin[sv.se$freq >= (n - n.cutoff)] = 'deletion'
sv.se$sin[sv.se$freq <= n.cutoff] = 'insertion'


g.nodes.prot.sin = g.nodes.prot
g.nodes.prot.sin[names(g.nodes.prot.sin) %in% sv.se$name[sv.se$sin != 'insertion'] ] = 'na'
g.cols['na'] = 'white'




set.seed(239)
p <- ggnet2(g.part, label = F, edge.color = "black", 
            # node.size = g.nodes.cnt[b.graph.names], 
            node.size = 1,
            color = g.nodes.prot.sin[b.graph.names],
            palette = g.cols,
            # mode = "kamadakawai"
            ) + guides(size = F)
p 

# 
# path.figures  = '/Volumes/Samsung_T5/vienn/work_te/'
# pdf(paste(path.figures, 'graph_sv_note_insertion.pdf', sep = ''), width = 6, height = 4)
# print(p)     # Plot 1 --> in the first page of PDF
# dev.off()


alpha.edta = rep(1, length(b.graph.names))
names(alpha.edta) = b.graph.names

sv.annot.adta = rowSums(sv.annot[,11:ncol(sv.annot)] > 0.7) > 0
sv.annot.adta = sv.annot.adta[sv.se$gr]
names(sv.annot.adta) = sv.se$name
sv.annot.adta = sv.annot.adta[sv.annot.adta]
alpha.edta[names(alpha.edta) %in% names(sv.annot.adta)] = 0


set.seed(239)
p <- ggnet2(g.part, label = F, edge.color = "black", 
            # node.size = g.nodes.cnt[b.graph.names], 
            node.size = 1,
            alpha=1-alpha.edta,
            color = g.nodes.prot[b.graph.names],
            palette = g.cols,
            # mode = "kamadakawai"
            ) + guides(size = F)
p 


pdf(paste(path.figures, 'graph_mob_note_edta.pdf', sep = ''), width = 6, height = 4)
print(p)     # Plot 1 --> in the first page of PDF
dev.off()

path.figures  = '/Volumes/Samsung_T5/vienn/work_te/'
pdf(paste(path.figures, 'graph_mob_note_edta_no_legend.pdf', sep = ''), width = 5, height = 5)
print(p+ theme(legend.position = "none"))     # Plot 1 --> in the first page of PDF
dev.off()

Plot with component ID



tmp.graph <- igraph::make_graph(t(sv.edges), directed = T)
tmp.graph <- igraph::simplify(tmp.graph)
tmp.comp <- igraph::components(tmp.graph)

size.limit = 5
comp.id = as.character(tmp.comp$membership)
names(comp.id) = names(tmp.comp$membership)
comp.id[tmp.comp$csize[tmp.comp$membership] < size.limit] = ''

names.te = names(g.nodes.prot)[g.nodes.prot %in% c('transpos', 'reverse')]

comp.id[!(names(comp.id) %in% names.te)] = ''

comp.id[duplicated(comp.id)] = ''


comp.remain = as.numeric(comp.id[comp.id != ''])
alpha = rep(0, length(b.graph.names))
names(alpha) = names(tmp.comp$membership)
alpha[tmp.comp$membership %in% comp.remain] = 1

set.seed(239)
p <- ggnet2(g.part, label = comp.id[b.graph.names], 
            label.color = "black",
            label.size = 3,
            edge.color = "grey", 
            alpha = alpha[b.graph.names],
            # node.size = g.nodes.cnt[b.graph.names], 
            node.size = 1,
            color = g.nodes.prot[b.graph.names],
            palette = g.cols,
            # mode = "kamadakawai"
            ) + guides(size = F)
p 


path.figures  = '/Volumes/Samsung_T5/vienn/work_te/'
pdf(paste(path.figures, 'graph_sv_note_numbers.pdf', sep = ''), width = 5, height = 5)
print(p + theme(legend.position = "none"))     # Plot 1 --> in the first page of PDF
dev.off()



# Order of components
cnt = table(tmp.comp$membership[tmp.comp$membership %in% comp.remain])
cnt = cnt[order(-cnt)]

CNV


cnv = readRDS('/Volumes/Samsung_T5/vienn/work_sv/similar_cnv_sv_on_accessions_cum_0.9.rds')

Plot one specific network


path.figures.examples  = '/Volumes/Samsung_T5/vienn/work_te/examples/'

# 
# tmp.graph <- igraph::make_graph(t(sv.edges), directed = T)
# tmp.graph <- igraph::simplify(tmp.graph)
# tmp.comp <- igraph::components(tmp.graph)
# 
# tmp.cnt = table(tmp.comp$membership)
# tmp.cnt = -sort(-tmp.cnt)

tmp.cnt = cnt

for(k in 1:length(tmp.cnt)){
  tmp.k = as.numeric(names(tmp.cnt)[k])
  tmp.names = names(tmp.comp$membership)[tmp.comp$membership == tmp.k]
  b.graph.sub = sv.edges[(sv.edges[,1] %in% tmp.names) & 
                          (sv.edges[,2] %in% tmp.names),]
  
  
  g.part.sub <- network(b.graph.sub, matrix.type = "edgelist", ignore.eval = FALSE, directed = TRUE)
  b.graph.names.sub = network.vertex.names(g.part.sub)
  
  
    
  b.graph.size.sub <- as.numeric(sub(".*\\|", "", b.graph.names.sub))
  names(b.graph.size.sub) = b.graph.names.sub
  # b.graph.size.sub = ceiling(log(b.graph.size.sub, 10))
  
  if((length(unique( g.nodes.prot[b.graph.names.sub])) == 1)){
    set.seed(20)
    p <- ggnet2(g.part.sub, label = b.graph.size.sub[b.graph.names.sub], edge.color = "black", 
                node.size = 15,
                arrow.gap = 0.07, arrow.size = 3,
                color = g.cols[g.nodes.prot[b.graph.names.sub][1]],
                ) + guides(size = F) +  ggtitle(paste('Component #', tmp.k))
    p
  } else {
    set.seed(20)
    p <- ggnet2(g.part.sub, label = b.graph.size.sub[b.graph.names.sub], edge.color = "black", 
                node.size = 15,
                arrow.gap = 0.07, arrow.size = 3,
                color = g.nodes.prot[b.graph.names.sub],
                palette = g.cols,
                ) + guides(size = F) +  ggtitle(paste('Component #', tmp.k))
    p
  }
  
 
  
  pdf(paste(path.figures.examples, 'graph_sv_example_',k,'_comp_',tmp.k,'.pdf', sep = ''), width = 5, height = 4)
  print(p + theme(legend.position = "none"))     # Plot 1 --> in the first page of PDF
  dev.off()
  
  # annotation
  annot.tmp = sv.prot[sv.prot$name %in% b.graph.names.sub,]
  # annot.tmp = annot.tmp[annot.tmp$transpos == 1,]
  
  write.table(annot.tmp, paste(path.figures.examples, 'graph_sv_example_',k,'_pblast.txt', sep = ''), 
              row.names = F, col.names = F, quote = F, sep = '\t')
  
  
  # if EDTA annotation exists
  sv.tmp = unique(c(b.graph.sub))
  sv.tmp.cut <- gsub("\\|.*", "", sv.tmp)
  sv.annot.tmp = sv.annot[sv.tmp.cut,]
  n.fix = 9
  sv.annot.tmp  = sv.annot.tmp[,c(1:n.fix,n.fix+which(colSums(sv.annot.tmp[,(n.fix+1):ncol(sv.annot.tmp)]) != 0))]
  rownames(sv.annot.tmp) = sv.tmp
    
  write.table(sv.annot.tmp, paste(path.figures.examples, 'graph_sv_example_',k,'_edta.txt', sep = ''), 
             row.names = F, quote = F, sep = '\t')
  
  # Copy0Number variation
  cnv.tmp = cnv[sv.tmp,]
  
  heatmap(cnv.tmp, col = colorRampPalette(c("white", "red"))(20))
  
}

Pie-chart of proteins

library(ggplot2)

data <- data.frame(
  type = c("no proteins", "TE-related", "Категория 2", "Категория 3", "Категория 4"),
  value = c(135, 63, 85, 133)
)

pie.chart <- ggplot(data, aes(x = "", y = value, fill = type)) +
  geom_bar(stat = "identity", width = 1) +
  coord_polar("y", start = 0) +
  theme_void()

pie.chart

Admixture groups

groups <- c(
  "germany",
  "south_sweden",
  "north_sweden",
  "south_sweden",
  "north_sweden",
  "germany",
  "western_europe",
  "central_europe",
  "italy_balkan_caucasus",
  "spain",
  "relict",
  "asia",
  "central_europe",
  "admixed",
  "spain",
  "relict",
  "italy_balkan_caucasus",
  "western_europe",
  "asia",
  "africa",
  "china",
  "china",
  "africa",
  "africa",
  "madeira",
  "madeira",
  "africa"
)

# Используем функцию table() для подсчета количества элементов в каждой группе
as.matrix(table(groups))

OLD

sunset <- colour("sunset")
discrete_rainbow <- colour("discrete rainbow")

file.te = '/Volumes/Samsung_T5/vienn/work/blast_tes_ann.txt'
sim.cutoff = 0.85
len.cutoff = 100

b = read.table(file.te, stringsAsFactors = F)
b = b[b$V1 != b$V8,]
b$len1 = as.numeric(sapply(b$V1, function(s) strsplit(s, '\\|')[[1]][7]))
b$len2 = as.numeric(sapply(b$V8, function(s) strsplit(s, '\\|')[[1]][7]))
b = b[b$len1 >= len.cutoff,]
b = b[b$len2 >= len.cutoff,]
b$comb = paste(b$V1, b$V8, sep = '^')

# Order positions in base
idx = b$V4 > b$V5
tmp = b[idx, 'V4']
b[idx, 'V4'] = b[idx, 'V5']
b[idx, 'V5'] = tmp

# --------------------------------------------------
# Get separately those, who has a unique coverage
comb.tbl = table(b$comb)
idx.uni = b$comb %in% names(comb.tbl)[comb.tbl == 1]
b.uni = b[idx.uni,]
b = b[!idx.uni,]

# This variable will be used later
b.uni$p1 = (b.uni$V3 - b.uni$V2 + 1) / b.uni$len1
b.uni$p2 = (b.uni$V5 - b.uni$V4 + 1) / b.uni$len2
b.uni = b.uni[(b.uni$p1 >= sim.cutoff) | (b.uni$p2 >= sim.cutoff),]

b.relations = data.frame(sub.te = b.uni$V1[b.uni$p1 >= sim.cutoff],
                         te = b.uni$V8[b.uni$p1 >= sim.cutoff], stringsAsFactors = F)
b.relations = rbind(b.relations,
                    data.frame(sub.te = b.uni$V8[b.uni$p2 >= sim.cutoff],
                               te = b.uni$V1[b.uni$p2 >= sim.cutoff], stringsAsFactors = F))
b.relations = unique(b.relations)

# --------------------------------------------------
# Min-max of the coverage to remove those, who are NOT in each other completely
b.cov = tapply(b$V2, b$comb, min)
b.cov = data.frame(comb = names(b.cov), V2 = b.cov)
b.cov$V3 = tapply(b$V3, b$comb, max)
b.cov$V4 = tapply(b$V4, b$comb, min)
b.cov$V5 = tapply(b$V5, b$comb, max)
b.cov$len1 = tapply(b$len1, b$comb, unique)
b.cov$len2 = tapply(b$len2, b$comb, unique)
b.cov$p1 = (b.cov$V3 - b.cov$V2 + 1) / b.cov$len1
b.cov$p2 = (b.cov$V5 - b.cov$V4 + 1) / b.cov$len2

comb.uncov = b.cov$comb[(b.cov$p1 < sim.cutoff) & (b.cov$p2 < sim.cutoff)]

b = b[!(b$comb %in% comb.uncov),]

# --------------------------------------------------
# Calculate the coverage directly for the first
b = b[order(b$V3),]
b = b[order(b$V2),]
b = b[order(b$comb),]

# Remove nested
idx = which((b$V3[-nrow(b)] > b$V3[-1]) & (b$comb[-nrow(b)] == b$comb[-1])) + 1
b1 = b[-idx,]

# Compute gaps
b1$gap = c(b1$V2[-1] - b1$V3[-nrow(b1)] - 1, 0)
b1$gap[b1$gap < 0] = 0
idx.diff.comb = which(b1$comb[-1] != b1$comb[-nrow(b1)])
b1$gap[idx.diff.comb] = 0

b.cov = tapply(b1$V2, b1$comb, min)
b.cov = data.frame(comb = names(b.cov), V2 = b.cov)
b.cov$V3 = tapply(b1$V3, b1$comb, max)
b.cov$len1 = tapply(b1$len1, b1$comb, unique)
b.cov$gap = tapply(b1$gap, b1$comb, sum)
b.cov$len1 = b.cov$len1 
b.cov$p1 = (b.cov$V3 - b.cov$V2 + 1 - b.cov$gap) / b.cov$len1
b.cov$V1 = tapply(b1$V1, b1$comb, unique)
b.cov$V8 = tapply(b1$V8, b1$comb, unique)

b.cov = b.cov[b.cov$p1 >= sim.cutoff,]


b.relations = rbind(b.relations,
                    data.frame(sub.te = b.cov$V1,
                               te = b.cov$V8, stringsAsFactors = F))


# --------------------------------------------------
# Calculate the coverage directly for the second
b = b[order(b$V5),]
b = b[order(b$V4),]
b = b[order(b$comb),]

# Remove nested
idx = which((b$V5[-nrow(b)] > b$V5[-1]) & (b$comb[-nrow(b)] == b$comb[-1])) + 1
b1 = b[-idx,]

# Compute gaps
b1$gap = c(b1$V4[-1] - b1$V5[-nrow(b1)] - 1, 0)
b1$gap[b1$gap < 0] = 0
idx.diff.comb = which(b1$comb[-1] != b1$comb[-nrow(b1)])
b1$gap[idx.diff.comb] = 0

b.cov = tapply(b1$V4, b1$comb, min)
b.cov = data.frame(comb = names(b.cov), V4 = b.cov)
b.cov$V5 = tapply(b1$V5, b1$comb, max)
b.cov$len2 = tapply(b1$len2, b1$comb, unique)
b.cov$gap = tapply(b1$gap, b1$comb, sum)
b.cov$len2 = b.cov$len2 
b.cov$p1 = (b.cov$V5 - b.cov$V4 + 1 - b.cov$gap) / b.cov$len2
b.cov$V1 = tapply(b1$V1, b1$comb, unique)
b.cov$V8 = tapply(b1$V8, b1$comb, unique)

b.cov = b.cov[b.cov$p1 >= sim.cutoff,]


b.relations = rbind(b.relations,
                    data.frame(sub.te = b.cov$V8,
                               te = b.cov$V1, stringsAsFactors = F))

  
b.relations = unique(b.relations)


b.relations

Define clusters

b.nodes = rbind(b.relations,
                    data.frame(sub.te = b.relations$te,
                               te = b.relations$sub.te))

b.nodes$comb = paste(b.nodes$sub.te, b.nodes$te, sep = '^')

comb.tbl = table(b.nodes$comb)
comb.back.and.foth = names(comb.tbl)[comb.tbl >= 2]
b.nodes = b.nodes[b.nodes$comb %in% comb.back.and.foth,]
b.nodes = unique(b.nodes[, c('sub.te', 'te')])


te.nodes <- igraph::make_graph(t(b.nodes), directed = T)
te.nodes <- igraph::simplify(te.nodes)
te.nodes.comp <- igraph::components(te.nodes)

nodes = paste('N', te.nodes.comp$membership, sep = '')
names(nodes) = names(te.nodes.comp$membership)

Identify family for each node


nodes.family = sapply(names(nodes), function(s) strsplit(s, '\\|')[[1]][6])

nodes.family.max = tapply(nodes.family, nodes, function(s){
  tbl = table(s)
  f = names(tbl)[tbl == max(tbl)]
  if(length(f) == 1){
    return(f)
  } else {
    return('Mix')
  }
})

nodes.family.max[nodes.family.max %in% c('DNA/Pogo', 'DNA/Tc1', 'DNA/Harbinger', 'DNA/En-Spm',
                     'DNA/HAT', 'DNA', 'DNA/Mariner')] = 'DNA'
nodes.family.max[nodes.family.max %in% c('RathE1_cons', 'RathE2_cons')] = 'DNA'
nodes.family.max[nodes.family.max %in% c('LINE/L1', 'LINE?')] = 'LINE'
nodes.family.max[nodes.family.max %in% c('Unassigned')] = 'Mix'
nodes.family.unique = unique(nodes.family.max)

Graph without singletons


b.graph.init = b.relations[(b.relations$sub.te %in% names(nodes)) & (b.relations$te %in% names(nodes)),]
b.graph = b.graph.init
b.graph = cbind(nodes[as.character(b.graph$sub.te)], nodes[as.character(b.graph$te)])
b.graph = unique(b.graph)


b.graph = b.graph[b.graph[,1] != b.graph[,2],]

# reduce indirect arrows
idx.remove = c()
for(i.edge in 1:nrow(b.graph)){
  if(i.edge %% 1000 == 0) print(i.edge)
  tmp.to = b.graph[b.graph[,1] == b.graph[i.edge,1],2]
  tmp.from = b.graph[b.graph[,2] == b.graph[i.edge,2],1]
  if(length(intersect(tmp.to, tmp.from)) > 0) idx.remove = c(idx.remove, i.edge)
}
idx.remove = unique(idx.remove)
b.graph = b.graph[-idx.remove,]


# te.graph <- igraph::make_graph(t(b.graph), directed = T)
# te.graph <- igraph::simplify(te.graph)
# te.graph.comp <- igraph::components(te.graph)


nodes.family.max.graph = nodes.family.max[names(nodes.family.max) %in% unique(c(b.graph[,1], b.graph[,2]))]

graph.cols = sunset(length(unique(nodes.family.max.graph)))

graph.cols = discrete_rainbow(length(unique(nodes.family.max.graph)))
names(graph.cols) = unique(nodes.family.max.graph)
g.part <- network(b.graph, matrix.type = "edgelist", ignore.eval = FALSE, directed = TRUE)
p <- ggnet2(g.part, label = FALSE, edge.color = "black", node.size = 1, 
            color = nodes.family.max.graph, palette = graph.cols,
            mode = "kamadakawai")# + guides(size = FALSE)
p

Graph WITH singletons



names.core = names(nodes.family.max.graph)

b.graph.init = b.relations
for(i in 1:2){
  b.graph.init[b.graph.init[,i] %in% names(nodes), i] = nodes[b.graph.init[b.graph.init[,i] %in% names(nodes), i]]
}

b.graph = unique(b.graph.init)
b.graph = b.graph[b.graph[,1] != b.graph[,2],]
b.graph = unique(b.graph)
# Verteces from the previous graph
b.graph = b.graph[(b.graph[,1] %in% names.core) | (b.graph[,2] %in% names.core),]


# reduce indirect arrows
idx.remove = c()
for(i.edge in 1:nrow(b.graph)){
  if(i.edge %% 1000 == 0) print(i.edge)
  tmp.to = b.graph[b.graph[,1] == b.graph[i.edge,1],2]
  tmp.from = b.graph[b.graph[,2] == b.graph[i.edge,2],1]
  if(length(intersect(tmp.to, tmp.from)) > 0) idx.remove = c(idx.remove, i.edge)
}
idx.remove = unique(idx.remove)
b.graph = b.graph[-idx.remove,]

te.graph <- igraph::make_graph(t(b.graph), directed = T)
d <- igraph::distances(te.graph)
# te.graph <- igraph::simplify(te.graph)
# te.graph.comp <- igraph::components(te.graph)

names.new = unique(setdiff(c(b.graph[,1], b.graph[,2]), names(nodes.family.max)))
# names.new.val = paste('G',1:length(names.new), sep = '')
# names(names.new.val) = names.new
# names.new.val = 

names.new.family = sapply(names.new, function(s) strsplit(s, '\\|')[[1]][6])
names.new.family[names.new.family %in% c('DNA/Pogo', 'DNA/Tc1', 'DNA/Harbinger', 'DNA/En-Spm',
                     'DNA/HAT', 'DNA', 'DNA/Mariner')] = 'DNA'
names.new.family[names.new.family %in% c('RathE1_cons', 'RathE2_cons')] = 'DNA'
names.new.family[names.new.family %in% c('LINE/L1', 'LINE?')] = 'LINE'
names.new.family[names.new.family %in% c('Unassigned')] = 'Mix'


nodes.family.max.add = c(nodes.family.max, names.new.family)
nodes.family.max.add = nodes.family.max.add[unique(c(b.graph[,1], b.graph[,2]))]

graph.cols = discrete_rainbow(length(unique(nodes.family.max.add)))
graph.cols = sample(graph.cols)
names(graph.cols) = unique(nodes.family.max.add)

g.part <- network(b.graph, matrix.type = "edgelist", ignore.eval = FALSE, directed = TRUE)
p <- ggnet2(g.part, label = FALSE, edge.color = "black", node.size = 0.5, 
            color = nodes.family.max.add,
            palette = graph.cols, mode = "kamadakawai")
p

TSNE



library(Rtsne)




d <- igraph::distances(te.graph)
d.max = max(d[!is.infinite(d)])

d[is.infinite(d)] = d.max * 1.3

tSNE <- Rtsne(d, is_distance = TRUE, dims = 2)

plot(tSNE$Y[,1], tSNE$Y[,2])
LS0tCnRpdGxlOiAiR3JhcGggb2YgVEVzIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgoKIyBTZXR1cApgYGB7ciwgbWVzc2FnZT1GQUxTRX0KIyBsaWJyYXJ5KGdncGxvdDIpCiMgbGlicmFyeShyZXNoYXBlMikKbGlicmFyeShnZ3BhdHRlcm4pCmxpYnJhcnkodmlyaWRpcykKbGlicmFyeShjb2xvclJhbXBzKQpsaWJyYXJ5KGdyaWRFeHRyYSkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KCdpZ3JhcGgnKQpsaWJyYXJ5KGdnbmV0KQpsaWJyYXJ5KG5ldHdvcmspCmxpYnJhcnkoa2hyb21hKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGdncHVicikKbGlicmFyeShCaW9zdHJpbmdzKQoKc291cmNlKCdzaW1pbGFyaXR5LlInKQpzb3VyY2UoJ3NlcWRvdHBsb3QuUicpCnNvdXJjZSgnbXNhcGxvdC5SJykKCnN1bnNldCA8LSBjb2xvdXIoInN1bnNldCIpCmRpc2NyZXRlX3JhaW5ib3cgPC0gY29sb3VyKCJkaXNjcmV0ZSByYWluYm93IikKCnBhdGguYmFzZSA9ICcuLi8uLi8uLi8nCnBhdGgud29yayA9IHBhc3RlKHBhdGguYmFzZSwgJzAyX2FuYWx5c2lzLzA0X3N2LzAxX2RhdGEvJywgc2VwID0gJycpCnBhdGgudGFpciA9IHBhc3RlKHBhdGguYmFzZSwgJzAxX2RhdGFfY29tbW9uLzAxX3RhaXIxMC8nLCBzZXAgPSAnJykKcGF0aC5maWd1cmVzID0gcGFzdGUocGF0aC5iYXNlLCAnMDJfYW5hbHlzaXMvMDRfc3YvMDNfZmlndXJlcy8nLCBzZXAgPSAnJykKcGF0aC5zdnMgPSBwYXN0ZShwYXRoLmJhc2UsICcwMV9kYXRhX2NvbW1vbi8wMl9hbm5vdF9kZW5vdm8vMDJfcGFubmFncmFtL3N2cy8nLCBzZXAgPSAnJykKIyBwYXRoLmdlbmVzID0gcGFzdGUocGF0aC5iYXNlLCAnMDFfZGF0YV9jb21tb24vMDJfYW5ub3RfZGVub3ZvLzAyX3Bhbm5hZ3JhbS9nZW5lcy8nLCBzZXAgPSAnJykKCiMgc2ltLmN1dG9mZiA9IDAuOQoKc2ltLmN1dG9mZiA9IDAuODUKCmBgYAoKIyBDb2xvcnMKYGBge3J9CgojIC0tLS0gQ29sb3JzIG9mIFRFIGNvbnRlbnQgLS0tLQp0ZS5jb250ZW50Lm5hbWVzID0gYygibm9URSIsICJpc1RFIiwgImhhc1RFIiwgImhhc1RFcGFydCIsICJpc1RFcGFydCIpCnRlLmNvbHMgPSBjKCcjRDhEOUNGJywgJyNFQjQ1NUYnLCAnIzdCNjA3OScsICcjM0M4REFEJywgJyM3OUI3NzMnKQpuYW1lcyh0ZS5jb2xzKSA9IHRlLmNvbnRlbnQubmFtZXMKCgojIC0tLS0gQ29sb3JzIG9mIEJMQVNUIGhpdHMgLS0tLQpjb2xvcnMuYmxhc3QuaGl0cyA8LSBjKCJpbiBncmFwaCIgPSAiIzY3NkZBMyIsCiAgICAgICAgICAgICJwYXJ0aWFsIG92ZXJsYXAiID0gIiNGRjlGMjkiLAogICAgICAgICAgICAiMSBzZWxmLWhpdCIgPSAiIzZFQkY4QiIsCiAgICAgICAgICAgICIwIGhpdHMiID0gIiNEODIxNDgiLAogICAgICAgICAgICAiaW4gZ3JhcGggYnV0IG5vdCBpbiBTVnMiID0gIiMxNTFEM0IiKQoKCiMgLS0tLSBDb2xvcnMgb2Yga25vd24gVEUgZmFtaWxpZXMgLS0tLQpmYW0ucGFsZXR0ZSA9IGMoKQpmYW0ucGFsZXR0ZVsnVW5hc3NpZ25lZCddID0gJ2dyZXknCmZhbS5wYWxldHRlWydNaXgnXSA9ICdncmV5MjAnCmZhbS5wYWxldHRlWydNaXggd2l0aCBIZWxpdHJvbiddID0gJyMyNjZEOTgnCmZhbS5wYWxldHRlWydIZWxpdHJvbiddID0gJyNCQ0FDREUnCmZhbS5wYWxldHRlWyJMVFIvQ29waWEiXSA9ICcjQkZEQjM4JwpmYW0ucGFsZXR0ZVsiTFRSL0d5cHN5Il0gPSAnIzU0QjQzNScKZmFtLnBhbGV0dGVbIkROQS9IQVQiXSA9ICcjRjlCNUQwJwpmYW0ucGFsZXR0ZVsiRE5BKyJdID0gJyNDODY1OEMnCmZhbS5wYWxldHRlWyJETkEiXSA9ICcjQzg2NThDJwpmYW0ucGFsZXR0ZVsiRE5BL011RFIiXSA9ICcjOTcxNTQ5JwoKCmZhbS5wYWxldHRlWyJMSU5FIl0gPSAnI0ZGQzI2RicKZmFtLnBhbGV0dGVbIlJhdGhFMS8yLzNfY29ucyJdID0gJyNDMzgxNTQnCmZhbS5wYWxldHRlWyJTSU5FIl0gPSAnIzg4NEEzOScKZmFtLnBhbGV0dGVbIlRFRyJdID0gJyM0RTM2MzYnCgoKIyBDb2xvcnMgZm9yIG5ldyBURXMKZy5jb2xzID0gZGlzY3JldGVfcmFpbmJvdyhsZW5ndGgodW5pcXVlKHN2Lm1lbWIkcHJvdCkpKQpuYW1lcyhnLmNvbHMpID0gdW5pcXVlKHN2Lm1lbWIkcHJvdCkKIyBuYW1lcyhnLmNvbHMpID0gYygnbm8gcHJvdCcsICJ1bmRlZmluZWQgcHJvdCIsICJyZXZlcnNlIiwgInRyYW5zcG9zIiwicmVwZWF0IiwiemluYyIsICJyZWNlcHRvciIsImRlZmluZWQgcHJvdCIpCgpnLmNvbHNbJ2Rpc2Vhc2UnXSA9ICdibGFjaycKIyBnLmNvbHNbJ2RlZmluZWQgcHJvdCddID0gJyNGMEI4NkUnCiMgZy5jb2xzWydyZXBlYXQnXSA9ICcjQ0FFMEFCJwojIGcuY29sc1snemluayddID0gJyMxQTVEMUEnCiMgZy5jb2xzWydyZWNlcHRvciddID0gJyNFRDdCN0InCgpnLmNvbHNbJ3ppbmMnXSA9ICcjRjQ1MDUwJwpnLmNvbHNbJ3JlcGVhdCddID0gJyNFQTkwNkMnCmcuY29sc1sncmVjZXB0b3InXSA9ICcjRjdEMDYwJwpnLmNvbHNbJ3JldmVyc2UnXSA9ICcjOThEOEFBJwpnLmNvbHNbJ2RlZmluZWQgcHJvdCddID0gJyM3QkFGREUnCgoKCmBgYAoKCgojIFRFcwpgYGB7cn0KCiMgTG9hZCBzaW1pbGFyaXR5IGZ1bmN0aW9uCgpibC5maWxlID0gcGFzdGUocGF0aC53b3JrLCduZXdfdGVfb25fdGUuZmFzdGEnLHNlcCA9ICcnKQpibC5yZXMgPSByZWFkLnRhYmxlKGJsLmZpbGUpCmJsLnJlcyA9IGJsLnJlc1tibC5yZXMkVjEgIT0gYmwucmVzJFY4LF0KCmJsLnJlcy5pbml0ID0gYmwucmVzCmJsLnJlcyA9IGJsLnJlc1tibC5yZXMkVjYgPj0gc2ltLmN1dG9mZiAqIDEwMCxdCgpyZXMubmVzdCA9IGZpbmROZXN0ZWRuZXNzKGJsLnJlcywgdXNlLnN0cmFuZCA9IEYpCgpyZXMubmVzdC5sZW4gPSBzYXBwbHkodW5pcXVlKGMocmVzLm5lc3QkVjEsIHJlcy5uZXN0JFY4KSksIGZ1bmN0aW9uKHMpIGFzLm51bWVyaWMoc3Ryc3BsaXQocywgJ1xcfCcpW1sxXV1bNV0pKQogIApyZXMubmVzdCRsZW4xID0gcmVzLm5lc3QubGVuW3Jlcy5uZXN0JFYxXQpyZXMubmVzdCRsZW44ID0gcmVzLm5lc3QubGVuW3Jlcy5uZXN0JFY4XQpyZXMubmVzdCRwMSA9IHJlcy5uZXN0JEMxIC8gcmVzLm5lc3QkbGVuMQpyZXMubmVzdCRwOCA9IHJlcy5uZXN0JEM4IC8gcmVzLm5lc3QkbGVuOAoKcmVzLm5lc3Quc2ltID0gcmVzLm5lc3RbKHJlcy5uZXN0JHAxID49IHNpbS5jdXRvZmYpIHwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgKHJlcy5uZXN0JHA4ID49IHNpbS5jdXRvZmYpLF0KYGBgCgojIyBIb3cgbWFueSBURXMgYXJlIGluIHRoZSBncmFwaApEaXN0cmlidXRpb24gYW1vbmcgZmFtaWxpZXMgYW5kIHN1YmZhbWlsaWVzCkRpc3RyaWJ1dGlvbiBhbW9uZyBsZW5ndGhzCmBgYHtyfQp0ZS5pbi5ncmFwaCA9IHVuaXF1ZShjKHJlcy5uZXN0JFYxLCByZXMubmVzdCRWOCkpCgojIFdoYXQgaXMgdGhlIGFjdHVhbCBudW1iZXIgb2YgVEVzCmZpbGUuY29udGVudCA8LSByZWFkTGluZXMoYmwuZmlsZSkKCnNlbGVjdGVkLmxpbmVzIDwtIGZpbGUuY29udGVudFtncmVwbCgiXiMgUXVlcnk6fGhpdHMgZm91bmQiLCBmaWxlLmNvbnRlbnQpXQpkZi5xdWVyeSA9IGRhdGEuZnJhbWUoYi5xdWVyeT1zZWxlY3RlZC5saW5lc1tzZXEoMSwgbGVuZ3RoKHNlbGVjdGVkLmxpbmVzKSwgYnkgPSAyKV0sCiAgICAgICAgICAgICAgICAgICAgICBiLmhpdHM9c2VsZWN0ZWQubGluZXNbc2VxKDIsIGxlbmd0aChzZWxlY3RlZC5saW5lcyksIGJ5ID0gMildKQoKZGYucXVlcnkkcXVlcnkgIDwtIGdzdWIoIl4jIFF1ZXJ5OiAoLiopIiwgIlxcMSIsIGRmLnF1ZXJ5JGIucXVlcnkpCmRmLnF1ZXJ5JGxlbiA8LSBhcy5udW1lcmljKHNhcHBseShzdHJzcGxpdChkZi5xdWVyeSRxdWVyeSwgIlxcfCIpLCBmdW5jdGlvbih4KSB4WzVdKSkKZGYucXVlcnkkaGl0cyA8LSBhcy5udW1lcmljKHN0cmluZ3I6OnN0cl9leHRyYWN0KGRmLnF1ZXJ5JGIuaGl0cywgIlxcZCsiKSkKZGYucXVlcnkkdmFsLmhpdHMgPSBkZi5xdWVyeSRoaXRzCmRmLnF1ZXJ5JHZhbC5oaXRzW2RmLnF1ZXJ5JHZhbC5oaXRzID49IDJdID0gMgpkZi5xdWVyeSR2YWwuaGl0c1tkZi5xdWVyeSRxdWVyeSAlaW4lIGJsLnJlcyRWOF0gPSAyCmRmLnF1ZXJ5JHZhbC5oaXRzW2RmLnF1ZXJ5JHF1ZXJ5ICVpbiUgdGUuaW4uZ3JhcGhdID0gMwpoaXQudmFsdWVzID0gYygnMCBoaXRzJywgJzEgc2VsZi1oaXQnLCAncGFydGlhbCBvdmVybGFwJywgJ2luIGdyYXBoJywgImluIGdyYXBoIGJ1dCBub3QgaW4gU1ZzIikKZGYucXVlcnkkcy5oaXRzID0gaGl0LnZhbHVlc1tkZi5xdWVyeSR2YWwuaGl0cysxXQpkZi5xdWVyeSRzLmhpdHMgPSBmYWN0b3IoZGYucXVlcnkkcy5oaXRzLCBsZXZlbHMgPSByZXYoaGl0LnZhbHVlcykpCmRmLnF1ZXJ5JGZhbWlseSA8LSBzYXBwbHkoc3Ryc3BsaXQoZGYucXVlcnkkcXVlcnksICJcXHwiKSwgZnVuY3Rpb24oeCkgeFs5XSkKZGYucXVlcnkkc3ViZmFtIDwtIHNhcHBseShzdHJzcGxpdChkZi5xdWVyeSRxdWVyeSwgIlxcfCIpLCBmdW5jdGlvbih4KSB4WzhdKQoKCgojIFRFcywgd2hpY2ggYXJlIG5vdCBpbiBTVnMKdGUuaW4uc3ZzID0gcmVhZC50YWJsZShwYXN0ZShwYXRoLndvcmssICdibGFzdF90ZXNfb25fc3YudHh0Jywgc2VwID0gJycpLCBzdHJpbmdzQXNGYWN0b3JzID0gRikKdGUucmVzdCA9IHNldGRpZmYoZGYucXVlcnkkcXVlcnksIHRlLmluLnN2cyRWMSkKdGUuaW4uc3ZzID0gcmVhZC50YWJsZShwYXN0ZShwYXRoLndvcmssICdibGFzdF9zdl9vbl90ZXMudHh0Jywgc2VwID0gJycpLCBzdHJpbmdzQXNGYWN0b3JzID0gRikKdGUucmVzdCA9IHNldGRpZmYodGUucmVzdCwgdGUuaW4uc3ZzJFY4KQpkZi5xdWVyeSRzLmhpdHNbZGYucXVlcnkkcXVlcnkgJWluJSB0ZS5yZXN0XSA9ICJpbiBncmFwaCBidXQgbm90IGluIFNWcyIKCgpwID0gZ2dwbG90KGRmLnF1ZXJ5LCBhZXMoeCA9IGxlbiwgZmlsbCA9IHMuaGl0cywgY29sb3IgPSBzLmhpdHMpKSArCiAgIyBnZW9tX2hpc3RvZ3JhbShhZXMoeSA9IC4uZGVuc2l0eS4uKSwgYWxwaGE9MC41LCBjb2xvciA9ICJibGFjayIsIGJpbnMgPSAzMCkgKwogICMgZ2VvbV9qaXR0ZXIoaGVpZ2h0ID0gMC4wMiwgd2lkdGggPSAwLCBhbHBoYSA9IDAuNykgKwogIGdlb21fZGVuc2l0eShhbHBoYSA9IDAuNSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGNvbG9ycy5ibGFzdC5oaXRzKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGNvbG9ycy5ibGFzdC5oaXRzKSArCiAgIHNjYWxlX3hfbG9nMTAoKSArCiAgbGFicyhmaWxsID0gTlVMTCwgY29sb3IgPSBOVUxMKSArCiAgeGxhYignbGVuZ3RoIG9mIFRFcycpICsgeWxhYignTm9ybWFsaXNlZCBkZW5zaXR5JykgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gYygxLCAxKSwgbGVnZW5kLmp1c3RpZmljYXRpb24gPSBjKDEsIDEpLAogICAgICAgICAgbGVnZW5kLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoY29sb3IgPSAiZ3JleTkwIikpCgpwCgpwZGYocGFzdGUocGF0aC5maWd1cmVzLCAndGVzX3NlbGZfYmxhc3RfbGVuX2RlbnNpdHkucGRmJywgc2VwID0gJycpLCB3aWR0aCA9IDUsIGhlaWdodCA9IDQpCnByaW50KHApICAgICAjIFBsb3QgMSAtLT4gaW4gdGhlIGZpcnN0IHBhZ2Ugb2YgUERGCmRldi5vZmYoKQoKCnRhYmxlKGRmLnF1ZXJ5JHZhbC5oaXRzKQpgYGAKCgojIyMgVEVzIG5vdCBpbiBTVnMKYGBge3J9CiMgVEVzIGluIHRlLWdyYXBoOiB0ZS5pbi5ncmFwaAojIFRFcyB3aGljaCBhcmUgaGF2ZSBubyBjb25uZWN0aW9uIHRvIFNWcwoKZGYgPSBhcy5kYXRhLmZyYW1lKHRhYmxlKGRmLnF1ZXJ5JHMuaGl0cykpCgpwID0gZ2dwbG90KGRmLCBhZXMoeCA9ICIiLCB5ID0gRnJlcSwgZmlsbCA9IFZhcjEpKSArCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiLCB3aWR0aD0xLCBhbHBoYSA9IDAuNykgKwogIGNvb3JkX3BvbGFyKCJ5Iiwgc3RhcnQ9MCkgKwogIGxhYnModGl0bGU9TlVMTCwgZmlsbD0iQ2F0ZWdvcmllcyIpICsKICB0aGVtZV92b2lkKCkrCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjb2xvcnMuYmxhc3QuaGl0cykgKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSBGcmVxLHggPSAxLjMpLCBwb3NpdGlvbiA9IHBvc2l0aW9uX3N0YWNrKHZqdXN0ID0gMC41KSkgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKQpwCgpwZGYocGFzdGUocGF0aC5maWd1cmVzLCAndGVzX3NlbGZfYmxhc3RfcGllX2NoYXJ0LnBkZicsIHNlcCA9ICcnKSwgd2lkdGggPSAzLCBoZWlnaHQgPSAzKQpwcmludChwKSAgICAgIyBQbG90IDEgLS0+IGluIHRoZSBmaXJzdCBwYWdlIG9mIFBERgpkZXYub2ZmKCkKCmBgYAoKIyMgRXhhbXBsZXMKIyMjIEV4YW1wbGVzIG5vIGhpdHMKYGBge3J9CgoKZGYucXVlcnkudG1wID0gZGYucXVlcnlbKGRmLnF1ZXJ5JHZhbC5oaXRzID09ICcwJyksXQoKY250LmluaXQgPSBjKHRhYmxlKGRmLnF1ZXJ5JGZhbWlseSkpCmNudC50bXAgPSBjKHRhYmxlKGRmLnF1ZXJ5LnRtcCRmYW1pbHkpKQoKY29tbW9uX25hbWVzIDwtIGludGVyc2VjdChuYW1lcyhjbnQuaW5pdCksIG5hbWVzKGNudC50bXApKQojINCh0L7Qt9C00LDQvdC40LUgZGF0YWZyYW1lINGC0L7Qu9GM0LrQviDQtNC70Y8g0YHQvtCy0L/QsNC00LDRjtGJ0LjRhSDQuNC80LXQvQpkZl9tYXRjaCA8LSBkYXRhLmZyYW1lKG5hbWVzID0gY29tbW9uX25hbWVzLCB2YWx1ZXMuaW5pdCA9IGNudC5pbml0W2NvbW1vbl9uYW1lc10sIAogICAgICAgICAgICAgICAgICAgICAgIHZhbHVlcy50bXAgPSBjbnQudG1wW2NvbW1vbl9uYW1lc10pCgoKZ3JhZGllbnRfY29sb3JzIDwtIGMoZGlzY3JldGVfcmFpbmJvdyhucm93KGRmX21hdGNoKSkpCm5hbWVzKGdyYWRpZW50X2NvbG9ycykgPSBOVUxMCgoKcCA9IGdncGxvdChkZl9tYXRjaCwgYWVzKHggPSB2YWx1ZXMuaW5pdCwgeSA9IHZhbHVlcy50bXAsIGxhYmVsID0gbmFtZXMsIGNvbG9yID0gbmFtZXMpKSArCiAgZ2VvbV9wb2ludCgpICsKICAjIGdlb21fdGV4dChoanVzdCA9IDAsIHZqdXN0ID0gMCkgKwogIGdncmVwZWw6Omdlb21fdGV4dF9yZXBlbChtYXgub3ZlcmxhcHMgPSAyMCkgKwogIHhsYWIoIkluaXRpYWwgY291bnRzIikgKwogIHlsYWIoImNvdW50cyBvZiBObyBoaXRzIikgKwogIHNjYWxlX3hfbG9nMTAoKSArCiAgc2NhbGVfeV9sb2cxMCgpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gZ3JhZGllbnRfY29sb3JzKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgZ3VpZGVzKGNvbG9yID0gRkFMU0UpICsKICB0aGVtZV9taW5pbWFsKCkKcAoKCnBkZihwYXN0ZShwYXRoLmZpZ3VyZXMsICd0ZXNfc2VsZl9zY2F0dGVyX25vX2hpdHMucGRmJywgc2VwID0gJycpLCB3aWR0aCA9IDUsIGhlaWdodCA9IDQpCnByaW50KHApICAgICAjIFBsb3QgMSAtLT4gaW4gdGhlIGZpcnN0IHBhZ2Ugb2YgUERGCmRldi5vZmYoKQoKIyBObyBoaXRzIGFuZCBsb25nCmxlbi5taW4gPSAxMDAKZGYucXVlcnkudG1wID0gZGYucXVlcnlbKGRmLnF1ZXJ5JHZhbC5oaXRzID09ICcwJykgJiAoZGYucXVlcnkkbGVuID49IGxlbi5taW4pLF0KCgpjbnQuaW5pdCA9IGModGFibGUoZGYucXVlcnkkZmFtaWx5KSkKY250LnRtcCA9IGModGFibGUoZGYucXVlcnkudG1wJGZhbWlseSkpCgpjb21tb25fbmFtZXMgPC0gaW50ZXJzZWN0KG5hbWVzKGNudC5pbml0KSwgbmFtZXMoY250LnRtcCkpCiMg0KHQvtC30LTQsNC90LjQtSBkYXRhZnJhbWUg0YLQvtC70YzQutC+INC00LvRjyDRgdC+0LLQv9Cw0LTQsNGO0YnQuNGFINC40LzQtdC9CmRmX21hdGNoIDwtIGRhdGEuZnJhbWUobmFtZXMgPSBjb21tb25fbmFtZXMsIHZhbHVlcy5pbml0ID0gY250LmluaXRbY29tbW9uX25hbWVzXSwgCiAgICAgICAgICAgICAgICAgICAgICAgdmFsdWVzLnRtcCA9IGNudC50bXBbY29tbW9uX25hbWVzXSkKCgpncmFkaWVudF9jb2xvcnMgPC0gYyhkaXNjcmV0ZV9yYWluYm93KG5yb3coZGZfbWF0Y2gpKSkKbmFtZXMoZ3JhZGllbnRfY29sb3JzKSA9IE5VTEwKCnAgPSBnZ3Bsb3QoZGZfbWF0Y2gsIGFlcyh4ID0gdmFsdWVzLmluaXQsIHkgPSB2YWx1ZXMudG1wLCBsYWJlbCA9IG5hbWVzLCBjb2xvciA9IG5hbWVzKSkgKwogIGdlb21fcG9pbnQoKSArCiAgIyBnZW9tX3RleHQoaGp1c3QgPSAwLCB2anVzdCA9IDApICsKICAjIGdncmVwZWw6Omdlb21fdGV4dF9yZXBlbChtYXgub3ZlcmxhcHMgPSAyMCkgKwogIGdncmVwZWw6Omdlb21fdGV4dF9yZXBlbChhZXMobGFiZWwgPSBwYXN0ZShuYW1lcywnKCcsdmFsdWVzLnRtcCwnKScsc2VwID0nJykpLCBtYXgub3ZlcmxhcHMgPSAyMCkgKwogIHhsYWIoIkluaXRpYWwgY291bnRzIikgKwogIHlsYWIoImNvdW50cyBvZiBObyBoaXRzIikgKwogIHNjYWxlX3hfbG9nMTAoKSArCiAgc2NhbGVfeV9sb2cxMCgpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gZ3JhZGllbnRfY29sb3JzKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgZ3VpZGVzKGNvbG9yID0gRkFMU0UpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIGdlb21fdGV4dChhZXMoeD0wLHk9SW5mLGhqdXN0PTAsIHZqdXN0PTMsCiAgICAgICAgICAgICAgICBsYWJlbD1wYXN0ZSgnTGVuZ3RoID49JywgbGVuLm1pbikpLCBjb2xvciA9ICdncmV5MjAnKQpwCgoKcGRmKHBhc3RlKHBhdGguZmlndXJlcywgJ3Rlc19zZWxmX3NjYXR0ZXJfbm9faGl0c19sb25nLnBkZicsIHNlcCA9ICcnKSwgd2lkdGggPSA1LCBoZWlnaHQgPSA0KQpwcmludChwKSAgICAgIyBQbG90IDEgLS0+IGluIHRoZSBmaXJzdCBwYWdlIG9mIFBERgpkZXYub2ZmKCkKCgpgYGAKCmBgYHtyfQoKaGVhZChkZi5xdWVyeS50bXBbZGYucXVlcnkudG1wJGZhbWlseSA9PSAnRE5BL011RFInLF0kcXVlcnkpCmBgYAoKCiMjIyBFeGFtcGxlcyBvbmUgc2VsZi1oaXQKIyMjIyBmYW1pbGllcwpgYGB7cn0KCgpkZi5xdWVyeS50bXAgPSBkZi5xdWVyeVsoZGYucXVlcnkkdmFsLmhpdHMgPT0gMSksXQoKY250LmluaXQgPSBjKHRhYmxlKGRmLnF1ZXJ5JGZhbWlseSkpCmNudC50bXAgPSBjKHRhYmxlKGRmLnF1ZXJ5LnRtcCRmYW1pbHkpKQoKY29tbW9uX25hbWVzIDwtIGludGVyc2VjdChuYW1lcyhjbnQuaW5pdCksIG5hbWVzKGNudC50bXApKQojINCh0L7Qt9C00LDQvdC40LUgZGF0YWZyYW1lINGC0L7Qu9GM0LrQviDQtNC70Y8g0YHQvtCy0L/QsNC00LDRjtGJ0LjRhSDQuNC80LXQvQpkZl9tYXRjaCA8LSBkYXRhLmZyYW1lKG5hbWVzID0gY29tbW9uX25hbWVzLCB2YWx1ZXMuaW5pdCA9IGNudC5pbml0W2NvbW1vbl9uYW1lc10sIAogICAgICAgICAgICAgICAgICAgICAgIHZhbHVlcy50bXAgPSBjbnQudG1wW2NvbW1vbl9uYW1lc10pCgoKZ3JhZGllbnRfY29sb3JzIDwtIGMoZGlzY3JldGVfcmFpbmJvdyhucm93KGRmX21hdGNoKSkpCm5hbWVzKGdyYWRpZW50X2NvbG9ycykgPSBOVUxMCgoKcCA9IGdncGxvdChkZl9tYXRjaCwgYWVzKHggPSB2YWx1ZXMuaW5pdCwgeSA9IHZhbHVlcy50bXAsIGxhYmVsID0gbmFtZXMsIGNvbG9yID0gbmFtZXMpKSArCiAgZ2VvbV9wb2ludCgpICsKICAjIGdlb21fdGV4dChoanVzdCA9IDAsIHZqdXN0ID0gMCkgKwogIGdncmVwZWw6Omdlb21fdGV4dF9yZXBlbChtYXgub3ZlcmxhcHMgPSAyMCkgKwogIHhsYWIoIkluaXRpYWwgY291bnRzIikgKwogIHlsYWIoIkNvdW50cyBpbiBcIjEgc2VsZi1oaXRzXCIgY2F0ZWdvcnkiKSArCiAgc2NhbGVfeF9sb2cxMCgpICsKICBzY2FsZV95X2xvZzEwKCkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBncmFkaWVudF9jb2xvcnMpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICBndWlkZXMoY29sb3IgPSBGQUxTRSkgKwogIHRoZW1lX21pbmltYWwoKQpwCgoKcGRmKHBhc3RlKHBhdGguZmlndXJlcywgJ3Rlc19zZWxmX3NjYXR0ZXJfMV9zZWxmaGl0c19mYW0ucGRmJywgc2VwID0gJycpLCB3aWR0aCA9IDUsIGhlaWdodCA9IDQpCnByaW50KHApICAgICAjIFBsb3QgMSAtLT4gaW4gdGhlIGZpcnN0IHBhZ2Ugb2YgUERGCmRldi5vZmYoKQpgYGAKCiMjIyMgc3ViZmFtaWxpZXMKYGBge3J9CgoKZGYucXVlcnkudG1wID0gZGYucXVlcnlbKGRmLnF1ZXJ5JHZhbC5oaXRzID09IDEpICYgKGRmLnF1ZXJ5JGxlbiA+PSA2MDApLF0KCmNudC5pbml0ID0gYyh0YWJsZShkZi5xdWVyeSRzdWJmYW0pKQpjbnQudG1wID0gYyh0YWJsZShkZi5xdWVyeS50bXAkc3ViZmFtKSkKCmNvbW1vbl9uYW1lcyA8LSBpbnRlcnNlY3QobmFtZXMoY250LmluaXQpLCBuYW1lcyhjbnQudG1wKSkKIyDQodC+0LfQtNCw0L3QuNC1IGRhdGFmcmFtZSDRgtC+0LvRjNC60L4g0LTQu9GPINGB0L7QstC/0LDQtNCw0Y7RidC40YUg0LjQvNC10L0KZGZfbWF0Y2ggPC0gZGF0YS5mcmFtZShuYW1lcyA9IGNvbW1vbl9uYW1lcywgdmFsdWVzLmluaXQgPSBjbnQuaW5pdFtjb21tb25fbmFtZXNdLCAKICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZXMudG1wID0gY250LnRtcFtjb21tb25fbmFtZXNdKQoKCiMgZ3JhZGllbnRfY29sb3JzIDwtIGMoZGlzY3JldGVfcmFpbmJvdyhucm93KGRmX21hdGNoKSkpCm5hbWVzKGdyYWRpZW50X2NvbG9ycykgPSBOVUxMCgoKcCA9IGdncGxvdChkZl9tYXRjaCwgYWVzKHggPSB2YWx1ZXMuaW5pdCwgeSA9IHZhbHVlcy50bXAsIGxhYmVsID0gbmFtZXMsIGNvbG9yID0gbmFtZXMpKSArCiAgZ2VvbV9wb2ludCgpICsKICAjIGdlb21fdGV4dChoanVzdCA9IDAsIHZqdXN0ID0gMCkgKwogIGdncmVwZWw6Omdlb21fdGV4dF9yZXBlbChtYXgub3ZlcmxhcHMgPSAyMCkgKwogIHhsYWIoIkluaXRpYWwgY291bnRzIikgKwogIHlsYWIoIkNvdW50cyBpbiBcIjEgc2VsZi1oaXRzXCIgY2F0ZWdvcnkiKSArCiAgc2NhbGVfeF9sb2cxMCgpICsKICBzY2FsZV95X2xvZzEwKCkgKwogICMgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGdyYWRpZW50X2NvbG9ycykgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogIGd1aWRlcyhjb2xvciA9IEZBTFNFKSArCiAgdGhlbWVfbWluaW1hbCgpCnAKCgpwZGYocGFzdGUocGF0aC5maWd1cmVzLCAndGVzX3NlbGZfc2NhdHRlcl8xX3NlbGZoaXRzX3N1YmZhbS5wZGYnLCBzZXAgPSAnJyksIHdpZHRoID0gNywgaGVpZ2h0ID0gNSkKcHJpbnQocCkgICAgICMgUGxvdCAxIC0tPiBpbiB0aGUgZmlyc3QgcGFnZSBvZiBQREYKZGV2Lm9mZigpCgoKYGBgCgojIyMjIGluZGl2aWR1YWxzIGZyb20gc3ViZmFtaWxpZXMKYGBge3J9CnMuc3ViZmFtID0gJ0FUUkVQOCcKCmRmLnF1ZXJ5LnRtcCA9IGRmLnF1ZXJ5WyhkZi5xdWVyeSRzdWJmYW0gPT0gcy5zdWJmYW0pICYgKGRmLnF1ZXJ5JGxlbiA+PSA2MDApLF0KZGYucXVlcnkudG1wCgoKYGBgCgoKCgoKIyMgQ3JlYXRpbmcgdGhlIGdyYXBoCmBgYHtyfQojIGFsbCBlZGdlcwppZHggPSByZXMubmVzdCRwMSA+PSBzaW0uY3V0b2ZmCmVkZ2VzID0gY2JpbmQocmVzLm5lc3QkVjFbaWR4XSwgcmVzLm5lc3QkVjhbaWR4XSkKaWR4ID0gcmVzLm5lc3QkcDggPj0gc2ltLmN1dG9mZgplZGdlcyA9IHJiaW5kKGVkZ2VzLCBjYmluZChyZXMubmVzdCRWOFtpZHhdLCByZXMubmVzdCRWMVtpZHhdKSkKdGUuZW5nZXMubmFtZXMgPSB1bmlxdWUoYyhlZGdlc1ssMV0sIGVkZ2VzWywyXSkpCnRlLmVuZ2VzLmZhbSA9IHNhcHBseSh0ZS5lbmdlcy5uYW1lcywgZnVuY3Rpb24ocykgc3Ryc3BsaXQocywgJ1xcfCcpW1sxXV1bOV0gKQoKdGUuZW5nZXMuZmFtW3RlLmVuZ2VzLmZhbSAlaW4lIGMoJ0ROQS9Qb2dvJywgJ0ROQS9UYzEnLCAnRE5BL0hhcmJpbmdlcicsICdETkEvRW4tU3BtJywKICAgICAgICAgICAgICAgICAgICAgJ0ROQS9IQVQnLCAnRE5BJywgJ0ROQS9NYXJpbmVyJyldID0gJ0ROQScKdGUuZW5nZXMuZmFtW3RlLmVuZ2VzLmZhbSAlaW4lIGMoJ1JhdGhFMV9jb25zJywgJ1JhdGhFMl9jb25zJywgJ1JhdGhFM19jb25zJyldID0gJ1JhdGhFMS8yLzNfY29ucycKdGUuZW5nZXMuZmFtW3RlLmVuZ2VzLmZhbSAlaW4lIGMoJ0xJTkUvTDEnLCAnTElORT8nKV0gPSAnTElORScKdGUuZW5nZXMuZmFtW3RlLmVuZ2VzLmZhbSAlaW4lIGMoJ1VuYXNzaWduZWQnKV0gPSAnTWl4Jwp0ZS5lbmdlcy5mYW1bdGUuZW5nZXMuZmFtICVpbiUgYygnUkMvSGVsaXRyb24nKV0gPSAnSGVsaXRyb24nCgplZGdlcyA9IGVkZ2VzW3RlLmVuZ2VzLmZhbVtlZGdlc1ssMV1dICE9ICdURUcnLF0KZWRnZXMgPSBlZGdlc1t0ZS5lbmdlcy5mYW1bZWRnZXNbLDJdXSAhPSAnVEVHJyxdCnRlLmVuZ2VzLm5hbWVzID0gdW5pcXVlKGMoZWRnZXNbLDFdLCBlZGdlc1ssMl0pKQoKCiMgbm9kZXMKaWR4ID0gKHJlcy5uZXN0JHAxID49IHNpbS5jdXRvZmYpICYgKHJlcy5uZXN0JHA4ID49IHNpbS5jdXRvZmYpCnRlLm5vZGVzID0gY2JpbmQocmVzLm5lc3QkVjFbaWR4XSwgcmVzLm5lc3QkVjhbaWR4XSkKdGUubm9kZXMgPSB0ZS5ub2Rlc1t0ZS5lbmdlcy5mYW1bdGUubm9kZXNbLDFdXSAhPSAnVEVHJyxdCnRlLm5vZGVzID0gdGUubm9kZXNbdGUuZW5nZXMuZmFtW3RlLm5vZGVzWywyXV0gIT0gJ1RFRycsXQoKdGUucmVzdCA9IHNldGRpZmYodGUuZW5nZXMubmFtZXMsIGModGUubm9kZXNbLDFdLCB0ZS5ub2Rlc1ssMl0pKQoKCnRlLm5vZGVzLmdyYXBoIDwtIGlncmFwaDo6bWFrZV9ncmFwaCh0KHRlLm5vZGVzKSwgZGlyZWN0ZWQgPSBUKQp0ZS5ub2Rlcy5ncmFwaCA8LSBpZ3JhcGg6OnNpbXBsaWZ5KHRlLm5vZGVzLmdyYXBoKQp0ZS5ub2Rlcy5jb21wIDwtIGlncmFwaDo6Y29tcG9uZW50cyh0ZS5ub2Rlcy5ncmFwaCkKCm5vZGVzID0gZGF0YS5mcmFtZShub2RlID0gcGFzdGUoJ04nLCB0ZS5ub2Rlcy5jb21wJG1lbWJlcnNoaXAsIHNlcCA9ICcnKSwgCiAgICAgICAgICAgICAgICAgICB0ZSA9IG5hbWVzKHRlLm5vZGVzLmNvbXAkbWVtYmVyc2hpcCkpCgpub2Rlcy5yZXN0ID0gZGF0YS5mcmFtZShub2RlID0gcGFzdGUoJ1InLCAoMTpsZW5ndGgodGUucmVzdCkpLCBzZXAgPSAnJyksIHRlID0gdGUucmVzdCkKbm9kZXMgPSByYmluZChub2Rlcywgbm9kZXMucmVzdCkKCnJvd25hbWVzKG5vZGVzKSA9IG5vZGVzJHRlCgoKbm9kZXMuY250ID0gZGF0YS5mcmFtZShjbnQgPSBjKHRhYmxlKG5vZGVzJG5vZGUpKSkKbm9kZXMuY250JG5vZGUgPSByb3duYW1lcyhub2Rlcy5jbnQpCm5vZGVzLmNudCRmYW0gPSBzYXBwbHkobm9kZXMuY250JG5vZGUsIGZ1bmN0aW9uKHMpewogIHMudGUgPSBub2RlcyR0ZVtub2RlcyRub2RlID09IHNdCiAgZmFtLnRlID0gdW5pcXVlKHRlLmVuZ2VzLmZhbVtzLnRlXSkKICBpZihsZW5ndGgoZmFtLnRlKSA9PSAxKXsKICAgIHJldHVybihmYW0udGUpCiAgfSBlbHNlIHsKICAgIGZhbS50ZSA9IHNldGRpZmYoZmFtLnRlLCAnVEVHJykKICAgIGlmKGxlbmd0aChmYW0udGUpID09IDEpIHJldHVybihmYW0udGUpCiAgICByZXR1cm4oJ01peCcpCiAgfQp9KQp0YWJsZShub2Rlcy5jbnQkZmFtKQoKCiMgUmVkZWZpbmUgZWRnZXMgYnV0IHdpdGggbm9kZSBuYW1lcwppZHguZW5kZXMgPSAoZWRnZXNbLDFdICVpbiUgbm9kZXMkdGUpICYgKGVkZ2VzWywyXSAlaW4lIG5vZGVzJHRlKQpiLmdyYXBoID0gY2JpbmQobm9kZXNbZWRnZXNbaWR4LmVuZGVzLDFdLCAnbm9kZSddLG5vZGVzW2VkZ2VzW2lkeC5lbmRlcywyXSwgJ25vZGUnXSkKYi5ncmFwaCA9IHVuaXF1ZShiLmdyYXBoKQojIGIuZ3JhcGggPSBiLmdyYXBoW2IuZ3JhcGhbLDFdICE9IGIuZ3JhcGhbLDJdLF0KYi5ncmFwaC51bmkgPSBiLmdyYXBoW2IuZ3JhcGhbLDFdID09IGIuZ3JhcGhbLDJdLF0KYi5ncmFwaCA9IGIuZ3JhcGhbYi5ncmFwaFssMV0gIT0gYi5ncmFwaFssMl0sXQoKbGVuZ3RoKHVuaXF1ZShjKGIuZ3JhcGhbLDFdLCBiLmdyYXBoWywyXSkpKQoKIyByZWR1Y2UgaW5kaXJlY3QgYXJyb3dzCmlkeC5yZW1vdmUgPSBjKCkKZm9yKGkuZWRnZSBpbiAxOm5yb3coYi5ncmFwaCkpewogIGlmKGkuZWRnZSAlJSAxMDAwID09IDApIHByaW50KGkuZWRnZSkKICB0bXAudG8gPSBiLmdyYXBoW2IuZ3JhcGhbLDFdID09IGIuZ3JhcGhbaS5lZGdlLDFdLDJdCiAgdG1wLmZyb20gPSBiLmdyYXBoW2IuZ3JhcGhbLDJdID09IGIuZ3JhcGhbaS5lZGdlLDJdLDFdCiAgaWYobGVuZ3RoKGludGVyc2VjdCh0bXAudG8sIHRtcC5mcm9tKSkgPiAwKSBpZHgucmVtb3ZlID0gYyhpZHgucmVtb3ZlLCBpLmVkZ2UpCn0KaWR4LnJlbW92ZSA9IHVuaXF1ZShpZHgucmVtb3ZlKQpiLmdyYXBoID0gYi5ncmFwaFstaWR4LnJlbW92ZSxdCiMgYi5ncmFwaCA9IHJiaW5kKGIuZ3JhcGgsIGIuZ3JhcGgudW5pKQoKIyBQcmludCBncmFwaAoKZy5ub2Rlcy5mYW0gPSBub2Rlcy5jbnQkZmFtCm5hbWVzKGcubm9kZXMuZmFtKSA9IG5vZGVzLmNudCRub2RlCmcubm9kZXMuY250ID0gbm9kZXMuY250JGNudApuYW1lcyhnLm5vZGVzLmNudCkgPSBub2Rlcy5jbnQkbm9kZQoKZy5jb2xzID0gZGlzY3JldGVfcmFpbmJvdyhsZW5ndGgodW5pcXVlKGcubm9kZXMuZmFtKSkpCm5hbWVzKGcuY29scykgPSB1bmlxdWUoZy5ub2Rlcy5mYW0pCgpiLmdyYXBoLmluaXQgPSBiLmdyYXBoCgoKZy5wYXJ0IDwtIG5ldHdvcmsoYi5ncmFwaCwgbWF0cml4LnR5cGUgPSAiZWRnZWxpc3QiLCBpZ25vcmUuZXZhbCA9IEZBTFNFLCBkaXJlY3RlZCA9IFRSVUUpCmIuZ3JhcGgubmFtZXMgPSBuZXR3b3JrLnZlcnRleC5uYW1lcyhnLnBhcnQpCmBgYAoKIyMjIE9sZCBjb2xvcnMKYGBge3J9CiMgcCA8LSBnZ25ldDIoZy5wYXJ0LCBsYWJlbCA9IEYsIGVkZ2UuY29sb3IgPSAiYmxhY2siLCAKIyAgICAgICAgICAgICBub2RlLnNpemUgPSBnLm5vZGVzLmNudFtiLmdyYXBoLm5hbWVzXSwgCiMgICAgICAgICAgICAgY29sb3IgPSBnLm5vZGVzLmZhbVtiLmdyYXBoLm5hbWVzXSwKIyAgICAgICAgICAgICBwYWxldHRlID0gZy5jb2xzLAojICAgICAgICAgICAgICMgbW9kZSA9ICJrYW1hZGFrYXdhaSIKIyAgICAgICAgICAgICApIAojIHAgKyBndWlkZXMoc2l6ZSA9IEYpCiMgCiMgIyAKIyAjIGIuZ3JhcGguZmFtID0gY2JpbmQoZy5ub2Rlcy5mYW1bYi5ncmFwaFssMV1dLCBnLm5vZGVzLmZhbVtiLmdyYXBoWywyXV0pCiMgIyBiLmdyYXBoLmZhbQojICMgCiMgIyB3aGljaCgoYi5ncmFwaC5mYW1bLDFdID09ICdETkEvTXVEUicpICYgKGIuZ3JhcGguZmFtWywxXSA9PSAnTElORScpKQojIAoKCmBgYAoKIyMjIE5ldyBGYW1pbHkgY29sb3JzCmBgYHtyfQojIGcuZmFtLm5hbWVzID0gc29ydCh1bmlxdWUoZy5ub2Rlcy5mYW0pKQojIGZhbS5wYWxldHRlID0gYygpCiMgaWR4LnBhbGxldGUgPSBjKCkKIyAKIyBpZHguZmFtIDwtIGdyZXAoIl5IZWxpdHJvbiIsIGcuZmFtLm5hbWVzLCB2YWx1ZSA9IEZBTFNFKQojIHRtcC5wYWxldHRlIDwtIGNvbG9yUmFtcFBhbGV0dGUoYygnI0JGQUNFMicsICcjMjY2RDk4JywgJyM0MjJCNzInKSkobGVuZ3RoKGlkeC5mYW0pKQojIGlkeC5wYWxsZXRlID0gYyhpZHgucGFsbGV0ZSwgaWR4LmZhbSkKIyBmYW0ucGFsZXR0ZSA9IGMoZmFtLnBhbGV0dGUsIHRtcC5wYWxldHRlKQojIAojIGlkeC5mYW0gPC0gZ3JlcCgiXkxUUiIsIGcuZmFtLm5hbWVzLCB2YWx1ZSA9IEZBTFNFKQojIHRtcC5wYWxldHRlIDwtIGNvbG9yUmFtcFBhbGV0dGUoYygnI0JGREIzOCcsICcjNTRCNDM1JykpKGxlbmd0aChpZHguZmFtKSkKIyBpZHgucGFsbGV0ZSA9IGMoaWR4LnBhbGxldGUsIGlkeC5mYW0pCiMgZmFtLnBhbGV0dGUgPSBjKGZhbS5wYWxldHRlLCB0bXAucGFsZXR0ZSkKIyAKIyBpZHguZmFtIDwtIGdyZXAoIl5ETkEiLCBnLmZhbS5uYW1lcywgdmFsdWUgPSBGQUxTRSkKIyB0bXAucGFsZXR0ZSA8LSBjb2xvclJhbXBQYWxldHRlKGMoJyNGOUI1RDAnLCAnIzk3MTU0OScpKShsZW5ndGgoaWR4LmZhbSkpCiMgaWR4LnBhbGxldGUgPSBjKGlkeC5wYWxsZXRlLCBpZHguZmFtKQojIGZhbS5wYWxldHRlID0gYyhmYW0ucGFsZXR0ZSwgdG1wLnBhbGV0dGUpCiMgCiMgaWR4LmZhbSA9IHNldGRpZmYoMTpsZW5ndGgoZy5mYW0ubmFtZXMpLCBpZHgucGFsbGV0ZSkKIyB0bXAucGFsZXR0ZSA8LSBjb2xvclJhbXBQYWxldHRlKGMoJyNGRkMyNkYnLCAnI0MzODE1NCcsICcjODg0QTM5JywgJyM0RTM2MzYnKSkobGVuZ3RoKGlkeC5mYW0pKQojIGlkeC5wYWxsZXRlID0gYyhpZHgucGFsbGV0ZSwgaWR4LmZhbSkKIyBmYW0ucGFsZXR0ZSA9IGMoZmFtLnBhbGV0dGUsIHRtcC5wYWxldHRlKQojIAojIG5hbWVzKGZhbS5wYWxldHRlKSA9IGcuZmFtLm5hbWVzW2lkeC5wYWxsZXRlXQojIGZhbS5wYWxldHRlWydVbmFzc2lnbmVkJ10gPSAnZ3JleScKIyBmYW0ucGFsZXR0ZVsnTWl4J10gPSAnYmxhY2snCiMgZmFtLnBhbGV0dGVbJ1RFRyddID0gJ2RhcmtncmVlbicKCgoKCmBgYAoKYGBge3J9CgpzZXQuc2VlZCgyMCkKcCA8LSBnZ25ldDIoZy5wYXJ0LCBsYWJlbCA9IEYsIGVkZ2UuY29sb3IgPSAiYmxhY2siLCAKICAgICAgICAgICAgbm9kZS5zaXplID0gZy5ub2Rlcy5jbnRbYi5ncmFwaC5uYW1lc10sIAogICAgICAgICAgICBjb2xvciA9IGcubm9kZXMuZmFtW2IuZ3JhcGgubmFtZXNdLAogICAgICAgICAgICBwYWxldHRlID0gZmFtLnBhbGV0dGUsCiAgICAgICAgICAgICMgbW9kZSA9ICJrYW1hZGFrYXdhaSIKICAgICAgICAgICAgKSAKcCA9IHAgKyBndWlkZXMoc2l6ZSA9IEYpIApwID0gcCArIGNvb3JkX2ZpeGVkKHJhdGlvID0gMSkKcAoKCgpwZGYocGFzdGUocGF0aC5maWd1cmVzLCAnZ3JhcGhfdGVzX2ZhbWlseS5wZGYnLCBzZXAgPSAnJyksIHdpZHRoID0gNSwgaGVpZ2h0ID0gNSkKcHJpbnQocCsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSkgICAgICMgUGxvdCAxIC0tPiBpbiB0aGUgZmlyc3QgcGFnZSBvZiBQREYKZGV2Lm9mZigpCgpwZGYocGFzdGUocGF0aC5maWd1cmVzLCAnZ3JhcGhfdGVzX2ZhbWlseV9sZWdlbmQucGRmJywgc2VwID0gJycpLCB3aWR0aCA9IDcsIGhlaWdodCA9IDUpCnByaW50KHApICAgICAjIFBsb3QgMSAtLT4gaW4gdGhlIGZpcnN0IHBhZ2Ugb2YgUERGCmRldi5vZmYoKQoKYGBgCgoKCiMjIFNlcGFyYXRlbHkgdmlzdWFsaXNlIGNvbm5lY3RlZCBjb21wb25lbnRzCmBgYHtyfQp0bXAuZ3JhcGggPC0gaWdyYXBoOjptYWtlX2dyYXBoKHQoYi5ncmFwaCksIGRpcmVjdGVkID0gVCkKdG1wLmdyYXBoIDwtIGlncmFwaDo6c2ltcGxpZnkodG1wLmdyYXBoKQp0bXAuY29tcCA8LSBpZ3JhcGg6OmNvbXBvbmVudHModG1wLmdyYXBoKQoKdG1wLmNudCA9IHRhYmxlKHRtcC5jb21wJG1lbWJlcnNoaXApCnRtcC5jbnQgPSAtc29ydCgtdG1wLmNudCkKaGVhZCh0bXAuY250KQoKayA9IDEKdG1wLmsgPSBhcy5udW1lcmljKG5hbWVzKHRtcC5jbnQpW2tdKQp0bXAubmFtZXMgPSBuYW1lcyh0bXAuY29tcCRtZW1iZXJzaGlwKVt0bXAuY29tcCRtZW1iZXJzaGlwID09IHRtcC5rXQpiLmdyYXBoLnN1YiA9IGIuZ3JhcGhbKGIuZ3JhcGhbLDFdICVpbiUgdG1wLm5hbWVzKSAmIAogICAgICAgICAgICAgICAgICAgICAgICAoYi5ncmFwaFssMl0gJWluJSB0bXAubmFtZXMpLF0KCmcucGFydC5zdWIuYmlnIDwtIG5ldHdvcmsoYi5ncmFwaC5zdWIsIG1hdHJpeC50eXBlID0gImVkZ2VsaXN0IiwgaWdub3JlLmV2YWwgPSBGQUxTRSwgZGlyZWN0ZWQgPSBUUlVFKQpiLmdyYXBoLm5hbWVzLnN1Yi5iaWcgPSBuZXR3b3JrLnZlcnRleC5uYW1lcyhnLnBhcnQuc3ViLmJpZykKCgpzZXQuc2VlZCgyMCkKcCA8LSBnZ25ldDIoZy5wYXJ0LnN1Yi5iaWcsIGxhYmVsID0gRiwgZWRnZS5jb2xvciA9ICJibGFjayIsIAogICAgICAgICAgICBub2RlLnNpemUgPSBnLm5vZGVzLmNudFtiLmdyYXBoLm5hbWVzLnN1Yi5iaWddLCAKICAgICAgICAgICAgY29sb3IgPSBnLm5vZGVzLmZhbVtiLmdyYXBoLm5hbWVzLnN1Yi5iaWddLAogICAgICAgICAgICBtb2RlID0gJ2thbWFkYWthd2FpJywKICAgICAgICAgICAgcGFsZXR0ZSA9IGZhbS5wYWxldHRlKSArIGd1aWRlcyhzaXplID0gRikKcC5iaWcudHlwZSA9IHAgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgojIHNldC5zZWVkKDIwKQojIHAgPC0gZ2duZXQyKGcucGFydC5zdWIuYmlnLCBsYWJlbCA9IEYsIGVkZ2UuY29sb3IgPSAiYmxhY2siLCAKIyAgICAgICAgICAgICBub2RlLnNpemUgPSBnLm5vZGVzLmNudFtiLmdyYXBoLm5hbWVzLnN1Yi5iaWddLCAKIyAgICAgICAgICAgICBjb2xvciA9IGcubm9kZXMuZmFtW2IuZ3JhcGgubmFtZXMuc3ViLmJpZ10sCiMgICAgICAgICAgICAgbW9kZSA9ICdrYW1hZGFrYXdhaScsCiMgICAgICAgICAgICAgcGFsZXR0ZSA9IGZhbS5wYWxldHRlKSArIGd1aWRlcyhzaXplID0gRikKIyBwLmJpZy5jb2xvciA9IHAgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgoKdG1wLmsgPSBhcy5udW1lcmljKG5hbWVzKHRtcC5jbnQpW2tdKQp0bXAubmFtZXMgPSBuYW1lcyh0bXAuY29tcCRtZW1iZXJzaGlwKVt0bXAuY29tcCRtZW1iZXJzaGlwICE9IHRtcC5rXQpiLmdyYXBoLnN1YiA9IGIuZ3JhcGhbKGIuZ3JhcGhbLDFdICVpbiUgdG1wLm5hbWVzKSAmIAogICAgICAgICAgICAgICAgICAgICAgICAoYi5ncmFwaFssMl0gJWluJSB0bXAubmFtZXMpLF0KCmcucGFydC5zdWIuc21hbGwgPC0gbmV0d29yayhiLmdyYXBoLnN1YiwgbWF0cml4LnR5cGUgPSAiZWRnZWxpc3QiLCBpZ25vcmUuZXZhbCA9IEZBTFNFLCBkaXJlY3RlZCA9IFRSVUUpCmIuZ3JhcGgubmFtZXMuc3ViLnNtYWxsID0gbmV0d29yay52ZXJ0ZXgubmFtZXMoZy5wYXJ0LnN1Yi5zbWFsbCkKCgpzZXQuc2VlZCgyMCkKcCA8LSBnZ25ldDIoZy5wYXJ0LnN1Yi5zbWFsbCwgbGFiZWwgPSBGLCBlZGdlLmNvbG9yID0gImJsYWNrIiwgCiAgICAgICAgICAgIG5vZGUuc2l6ZSA9IGcubm9kZXMuY250W2IuZ3JhcGgubmFtZXMuc3ViLnNtYWxsXSwgCiAgICAgICAgICAgIGNvbG9yID0gZy5ub2Rlcy5mYW1bYi5ncmFwaC5uYW1lcy5zdWIuc21hbGxdLAogICAgICAgICAgICAjIG1vZGUgPSAna2FtYWRha2F3YWknLAogICAgICAgICAgICBwYWxldHRlID0gZmFtLnBhbGV0dGUpICsgZ3VpZGVzKHNpemUgPSBGKQpwLnNtYWxsLnR5cGUgPXAgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgojIHNldC5zZWVkKDIwKQojIHAgPC0gZ2duZXQyKGcucGFydC5zdWIuc21hbGwsIGxhYmVsID0gRiwgZWRnZS5jb2xvciA9ICJibGFjayIsIAojICAgICAgICAgICAgIG5vZGUuc2l6ZSA9IGcubm9kZXMuY250W2IuZ3JhcGgubmFtZXMuc3ViLnNtYWxsXSwgCiMgICAgICAgICAgICAgY29sb3IgPSBnLm5vZGVzLmZhbVtiLmdyYXBoLm5hbWVzLnN1Yi5zbWFsbF0sCiMgICAgICAgICAgICAgIyBtb2RlID0gJ2thbWFkYWthd2FpJywKIyAgICAgICAgICAgICBwYWxldHRlID0gZmFtLnBhbGV0dGUpICsgZ3VpZGVzKHNpemUgPSBGKQojIHAuc21hbGwuY29sb3IgPSBwICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKCgpgYGAKIyMjIFBsb3RzCmBgYHtyfQpwLmJpZy50eXBlCnAuc21hbGwudHlwZQoKCnBkZihwYXN0ZShwYXRoLmZpZ3VyZXMsICdncmFwaF90ZXNfZmFtaWx5X3NtYWxsLnBkZicsIHNlcCA9ICcnKSwgd2lkdGggPSA5LCBoZWlnaHQgPSA5KQpwcmludChwLnNtYWxsLnR5cGUpICAgICAjIFBsb3QgMSAtLT4gaW4gdGhlIGZpcnN0IHBhZ2Ugb2YgUERGCmRldi5vZmYoKQoKcGRmKHBhc3RlKHBhdGguZmlndXJlcywgJ2dyYXBoX3Rlc19mYW1pbHlfYmlnLnBkZicsIHNlcCA9ICcnKSwgd2lkdGggPSA1LCBoZWlnaHQgPSA1KQpwcmludChwLmJpZy50eXBlKSAgICAgIyBQbG90IDEgLS0+IGluIHRoZSBmaXJzdCBwYWdlIG9mIFBERgpkZXYub2ZmKCkKCmBgYAoKIyBTdG9wIGZvciB0aGUgcGFwZXIKYGBge3J9CnN0b3AoKQpgYGAKCgojIyBTcGVjaWZpYyBURSBmYW1pbGllcwojIyMgR3JhcGggb2Ygb25lIGZhbWlseQoKYGBge3J9CnNvcnQoLXRhYmxlKGRmLnF1ZXJ5JHN1YmZhbVsoZGYucXVlcnkkdmFsLmhpdHMgPT0gMykgJiAoZGYucXVlcnkkZmFtaWx5ID09ICdMVFIvQ29waWEnKV0pKQpgYGAKCgoKYGBge3J9CgojIG9uZS50ZS5mYW0gPSAnQlJPRFlBR0ExJwojIG9uZS50ZS5mYW0gPSAnQlJPRFlBR0EyJwojIG9uZS50ZS5mYW0gPSAnSEVMSVRST05ZMUQnCiMgb25lLnRlLmZhbSA9ICdIRUxJVFJPTlkzJwpvbmUudGUuZmFtID0gJ0FUQ09QSUE0MScKcXVlcnkuZmFtID0gZGYucXVlcnkkcXVlcnlbZGYucXVlcnkkc3ViZmFtID09IG9uZS50ZS5mYW1dCgoKb25lLnRlLmZhbSA9ICdBVENPUElBNDEnCnF1ZXJ5LmZhbSA9IGRmLnF1ZXJ5JHF1ZXJ5W2RmLnF1ZXJ5JHN1YmZhbSA9PSBvbmUudGUuZmFtXQoKcmVzLm5lc3QuZmFtcCA9IHJlcy5uZXN0WyhyZXMubmVzdCRWMSAlaW4lIHF1ZXJ5LmZhbSkgfCAocmVzLm5lc3QkVjggJWluJSBxdWVyeS5mYW0pLF0KCgppZHggPSByZXMubmVzdC5mYW1wJHAxID49IHNpbS5jdXRvZmYKZWRnZXMgPSBjYmluZChyZXMubmVzdC5mYW1wJFYxW2lkeF0sIHJlcy5uZXN0LmZhbXAkVjhbaWR4XSkKaWR4ID0gcmVzLm5lc3QuZmFtcCRwOCA+PSBzaW0uY3V0b2ZmCmVkZ2VzID0gcmJpbmQoZWRnZXMsIGNiaW5kKHJlcy5uZXN0LmZhbXAkVjhbaWR4XSwgcmVzLm5lc3QuZmFtcCRWMVtpZHhdKSkKCgp0ZS5lbmdlcy5uYW1lcyA9IHVuaXF1ZShjKGVkZ2VzWywxXSwgZWRnZXNbLDJdKSkKdGUuZW5nZXMuZmFtID0gc2FwcGx5KHRlLmVuZ2VzLm5hbWVzLCBmdW5jdGlvbihzKSBzdHJzcGxpdChzLCAnXFx8JylbWzFdXVs5XSApCnRlLmVuZ2VzLmZhbVt0ZS5lbmdlcy5mYW0gJWluJSBjKCdETkEvUG9nbycsICdETkEvVGMxJywgJ0ROQS9IYXJiaW5nZXInLCAnRE5BL0VuLVNwbScsCiAgICAgICAgICAgICAgICAgICAgICdETkEvSEFUJywgJ0ROQScsICdETkEvTWFyaW5lcicpXSA9ICdETkEnCnRlLmVuZ2VzLmZhbVt0ZS5lbmdlcy5mYW0gJWluJSBjKCdSYXRoRTFfY29ucycsICdSYXRoRTJfY29ucycsICdSYXRoRTNfY29ucycpXSA9ICdSYXRoRTEvMi8zX2NvbnMnCnRlLmVuZ2VzLmZhbVt0ZS5lbmdlcy5mYW0gJWluJSBjKCdMSU5FL0wxJywgJ0xJTkU/JyldID0gJ0xJTkUnCnRlLmVuZ2VzLmZhbVt0ZS5lbmdlcy5mYW0gJWluJSBjKCdVbmFzc2lnbmVkJyldID0gJ01peCcKdGUuZW5nZXMuZmFtW3RlLmVuZ2VzLmZhbSAlaW4lIGMoJ1JDL0hlbGl0cm9uJyldID0gJ0hlbGl0cm9uJwoKZy5wYXJ0IDwtIG5ldHdvcmsoZWRnZXMsIG1hdHJpeC50eXBlID0gImVkZ2VsaXN0IiwgaWdub3JlLmV2YWwgPSBGQUxTRSwgZGlyZWN0ZWQgPSBUUlVFKQpiLmdyYXBoLm5hbWVzID0gbmV0d29yay52ZXJ0ZXgubmFtZXMoZy5wYXJ0KQpiLmdyYXBoLmxlbiA9IGFzLm51bWVyaWMoc2FwcGx5KHN0cnNwbGl0KGIuZ3JhcGgubmFtZXMsICJcXHwiKSwgZnVuY3Rpb24oeCkgeFs1XSkpCgoKbGFiZWwuZmFtaWx5ID0gc2FwcGx5KHN0cnNwbGl0KGIuZ3JhcGgubmFtZXMsICJcXHwiKSwgZnVuY3Rpb24oeCkgeFs4XSkKbGFiLmNvbHMgPSBjKCcjM0YyRTNFJywgIndoaXRlIikKbGFiZWwuY29sb3IgPSBsYWIuY29sc1sobGFiZWwuZmFtaWx5ID09IG9uZS50ZS5mYW0pICsgMV0KCnNldC5zZWVkKDIwKQpwIDwtIGdnbmV0MihnLnBhcnQsIGxhYmVsID0gYi5ncmFwaC5sZW4sIGVkZ2UuY29sb3IgPSAiYmxhY2siLCAKICAgICAgICAgICAgIG5vZGUuc2l6ZSA9IDE1LAogICAgICAgICAgICBhbHBoYT0wLjgsCiAgICAgICAgICAgIGFycm93LmdhcCA9IDAuMDE1LAogICAgICAgICAgICBhcnJvdy5zaXplID0gNSwKICAgICAgICAgICAgbGFiZWwuY29sb3IgPSBsYWJlbC5jb2xvciwKICAgICAgICAgICAgIyBub2RlLnNpemUgPSBnLm5vZGVzLmNudFtiLmdyYXBoLm5hbWVzXSwgCiAgICAgICAgICAgIGNvbG9yID0gdGUuZW5nZXMuZmFtW2IuZ3JhcGgubmFtZXNdLAogICAgICAgICAgICBwYWxldHRlID0gZmFtLnBhbGV0dGUsCiAgICAgICAgICAgICMgbW9kZSA9ICJrYW1hZGFrYXdhaSIKICAgICAgICAgICAgKSArIGd1aWRlcyhzaXplID0gRikKcCAKCnBkZihwYXN0ZShwYXRoLmZpZ3VyZXMsICdyZWFsX3Rlc19zdWJmYW1fJywgb25lLnRlLmZhbSwgJy5wZGYnLCBzZXAgPSAnJyksIHdpZHRoID0gMjAsIGhlaWdodCA9IDE4KQpwcmludChwKSAgICAgIyBQbG90IDEgLS0+IGluIHRoZSBmaXJzdCBwYWdlIG9mIFBERgpkZXYub2ZmKCkKCnNldC5zZWVkKDIwKQpwIDwtIGdnbmV0MihnLnBhcnQsIGxhYmVsID0gYi5ncmFwaC5uYW1lcywgZWRnZS5jb2xvciA9ICJibGFjayIsCiAgICAgICAgICAgICBub2RlLnNpemUgPSAxNSwKICAgICAgICAgICAgYWxwaGE9MC44LAogICAgICAgICAgICBhcnJvdy5nYXAgPSAwLjAxNSwKICAgICAgICAgICAgYXJyb3cuc2l6ZSA9IDUsCiAgICAgICAgICAgICMgbGFiZWwuY29sb3IgPSBsYWJlbC5jb2xvciwKICAgICAgICAgICAgIyBub2RlLnNpemUgPSBnLm5vZGVzLmNudFtiLmdyYXBoLm5hbWVzXSwKICAgICAgICAgICAgY29sb3IgPSB0ZS5lbmdlcy5mYW1bYi5ncmFwaC5uYW1lc10sCiAgICAgICAgICAgIHBhbGV0dGUgPSBmYW0ucGFsZXR0ZSwKICAgICAgICAgICAgIyBtb2RlID0gImthbWFkYWthd2FpIgogICAgICAgICAgICApICsgZ3VpZGVzKHNpemUgPSBGKQoKcGRmKHBhc3RlKHBhdGguZmlndXJlcywgJ3JlYWxfdGVzX3N1YmZhbV8nLCBvbmUudGUuZmFtLCAnX25hbWVzLnBkZicsIHNlcCA9ICcnKSwgd2lkdGggPSA1MCwgaGVpZ2h0ID0gNDkpCnByaW50KHApICAgICAjIFBsb3QgMSAtLT4gaW4gdGhlIGZpcnN0IHBhZ2Ugb2YgUERGCmRldi5vZmYoKQoKYGBgCgoKIyMgRG90cGxvdHMKCiMjIyBSZWFkIFRFIHNlcXVlbmNlcwpgYGB7cn0KZmlsZS50ZS5mYXN0YSA9IHBhc3RlKHBhdGgudGFpciwgJ25ld190ZS5mYXN0YScsIHNlcCA9ICcnKQp0ZS5mYXN0YSA9IHNlcWlucjo6cmVhZC5mYXN0YShmaWxlLnRlLmZhc3RhKQp0ZS5uYW1lcyA9IG5hbWVzKHRlLmZhc3RhKQp0ZS5mYXN0YSA9IHNlcWlucjo6Z2V0U2VxdWVuY2UodGUuZmFzdGEpCm5hbWVzKHRlLmZhc3RhKSA9IHRlLm5hbWVzCmBgYAoKIyMjIE9uZSBwYWlyd2lzZSBleGFtcGxlCmBgYHtyfQp3c2l6ZSA9IDEwCm5tYXRjaCA9IDgKCnNlcTEgPSB0ZS5mYXN0YVtbYi5ncmFwaC5uYW1lc1sxXV1dCnNlcTIgPSB0ZS5mYXN0YVtbYi5ncmFwaC5uYW1lc1sxXV1dCgoKbmFtZTEgPSAndGV8MTIzODQ3NjN8MTIzODUyNjJ8NHw1MDB8K3xBVDRURTU3NTgwfEJST0RZQUdBMUF8RE5BL011RFInCm5hbWUyID0gJ3RlfDEzNjc0OTE3fDEzNjc1MjcxfDF8MzU1fCt8QVQxVEU0NDc2MHxCUk9EWUFHQTF8RE5BL011RFInCgojIG5hbWUxID0gJ3RlfDEwNTkyMTExfDEwNTkyNjY0fDF8NTU0fC18QVQxVEUzNDI2NXxCUk9EWUFHQTJ8RE5BL011RFInCiMgbmFtZTIgPSAndGV8ODc0MzIzOHw4NzQ0MjYzfDR8MTAyNnwtfEFUNFRFMzkwNDV8SEVMSVRST05ZMUR8UkMvSGVsaXRyb24nCgpuYW1lMSA9ICd0ZXw2MjgzMTk4fDYyODQ0MjF8NHwxMjI0fC18QVQ0VEUyNjcxMHxBVFJFUDE1fFJDL0hlbGl0cm9uJwpuYW1lMiA9ICd0ZXw2MjgzMTk4fDYyODQ0MjF8NHwxMjI0fC18QVQ0VEUyNjcxMHxBVFJFUDE1fFJDL0hlbGl0cm9uJwoKc2VxMSA9IHRlLmZhc3RhW1tuYW1lMV1dCnNlcTIgPSB0ZS5mYXN0YVtbbmFtZTJdXQoKcCA9IGRvdHBsb3Qoc2VxMSwgc2VxMiwgd3NpemUsIG5tYXRjaCkKCnAgPSBwICsgYW5ub3RhdGUoInRleHQiLCB4ID0gLUluZiwgeSA9IEluZiwgbGFiZWwgPSBwYXN0ZSgnd3NpemU9Jyx3c2l6ZSwnXG5ubWF0Y2g9JyxubWF0Y2gsIHNlcCA9ICcnKSwgCiAgICAgICAgICAgICBoanVzdCA9IC0wLjEsIHZqdXN0ID0gMS4xKQoKcAoKcGRmKHBhc3RlKHBhdGguZmlndXJlcywgJ3BhaXJ3aXNlXycsJy5wZGYnLCBzZXAgPSAnJyksIHdpZHRoID0gNSwgaGVpZ2h0ID0gNSkKcHJpbnQocCkgICAgICMgUGxvdCAxIC0tPiBpbiB0aGUgZmlyc3QgcGFnZSBvZiBQREYKZGV2Lm9mZigpCgoKYGBgCgojIyMgb25lIFZTIGFsbApgYGB7cn0KCndzaXplID0gMTAKbm1hdGNoID0gOAoKbmFtZTAgPSAndGV8MTE2ODM1NjV8MTE2ODk4MjF8M3w2MjU3fCt8QVQzVEU0ODU0MHxBVENPUElBOTV8TFRSL0NvcGlhJwpuYW1lMCA9ICd0ZXwxNjY5MTc0OHwxNjY5NTE1NHwxfDM0MDd84oiSfEFUMVRFNTUwNzB8QVRDT1BJQTQxfExUUi9Db3BpYScKbmFtZTAgPSBnc3ViKCfiiJInLCAiLSIsIG5hbWUwKQoKCm9uZS50ZS5mYW0gPSBzdHJzcGxpdChuYW1lMCwgJ1xcfCcpW1sxXV1bOF0KIyBvbmUudGUuZmFtID0gJ0JST0RZQUdBMicKcXVlcnkuZmFtID0gZGYucXVlcnkkcXVlcnlbZGYucXVlcnkkc3ViZmFtID09IG9uZS50ZS5mYW1dCnF1ZXJ5LmZhbSA9IHF1ZXJ5LmZhbVsocXVlcnkuZmFtICVpbiUgcmVzLm5lc3Quc2ltJFYxKSB8IChxdWVyeS5mYW0gJWluJSByZXMubmVzdC5zaW0kVjIpXQoKbmFtZXMuYWxsID0gc2V0ZGlmZihxdWVyeS5mYW0sIG5hbWUwKQoKcC5hbGwgPSBsaXN0KCkKZm9yKG5hbWUyIGluIG5hbWVzLmFsbCl7CiAgIyBtZXNzYWdlKG5hbWUyKQogIHNlcTEgPSB0ZS5mYXN0YVtbbmFtZTBdXQogIHNlcTIgPSB0ZS5mYXN0YVtbbmFtZTJdXQogIAogIHMxID0gc3Ryc3BsaXQobmFtZTAsICdcXHwnKVtbMV1dWzddCiAgczIgPSBzdHJzcGxpdChuYW1lMiwgJ1xcfCcpW1sxXV1bN10KICBwID0gZG90cGxvdChzZXExLCBzZXEyLCB3c2l6ZSwgbm1hdGNoKSArIHhsYWIoczEpICsgeWxhYihzMikKICBwLmFsbFtbbmFtZTJdXSA9IHAKfQojIAojIHBwID0gZ3JpZC5hcnJhbmdlKGdyb2JzID0gcC5hbGwsIG5jb2wgPSAxMykgIyMgZGlzcGxheSBwbG90CiMgCiMgCiMgcGRmKHBhc3RlKHBhdGguZmlndXJlcywgJ3BhaXJ3aXNlX2FsbCcsJy5wZGYnLCBzZXAgPSAnJyksIHdpZHRoID0gNTAsIGhlaWdodCA9IDUwKQojIHByaW50KHBwKSAgICAgIyBQbG90IDEgLS0+IGluIHRoZSBmaXJzdCBwYWdlIG9mIFBERgojIGRldi5vZmYoKQoKczAgPSBwYXN0ZTAoc3Ryc3BsaXQobmFtZTAsICdcXHwnKVtbMV1dWzc6OV0sIGNvbGxhcHNlID0gJ18nKQpzMCA9IGdzdWIoIi8iLCAiLSIsIHMwKQpwZGYocGFzdGUocGF0aC5maWd1cmVzLCAncGFpcndpc2VfYWxsXycsczAsJy5wZGYnLCBzZXAgPSAnJyksIHdpZHRoID0gNTAsIGhlaWdodCA9IDUwKQpncmlkLmFycmFuZ2UoZ3JvYnMgPSBwLmFsbCwgbmNvbCA9IGNlaWxpbmcoc3FydChsZW5ndGgocC5hbGwpKSkpICMgV3JpdGUgdGhlIGdyaWQuYXJyYW5nZSBpbiB0aGUgZmlsZQpkZXYub2ZmKCkgIyBDbG9zZSB0aGUgZmlsZQoKYGBgCgoKIyMjIG9uZSBjb25uZWN0ZWQgY29tcG9uZW50CmBgYHtyfQoKd3NpemUgPSAxMApubWF0Y2ggPSA4CgoKbmFtZTAgPSAndGV8NjIwNTYyMXw2MjA2MTg0fDJ8NTY0fOKIknxBVDJURTI1MjU1fEhFTElUUk9OWTFEfFJDL0hlbGl0cm9uJwpuYW1lMCA9ICd0ZXwxNDE4OTI1NnwxNDE5MDI2Nnw1fDEwMTF8LXxBVDVURTUwNzAwfEhFTElUUk9OWTN8UkMvSGVsaXRyb24nCm5hbWUwID0gJ3RlfDEyNTEzMjM5fDEyNTEzODI0fDF8NTg2fCt8QVQxVEU0MDcyNXxBVEhJTEE0QXxMVFIvR3lwc3knCm5hbWUwID0gJ3RlfDExNjQ3NDI2fDExNjQ4OTEyfDF8MTQ4N3wrfEFUMVRFMzc3MDV8QVRSRVA3fFJDL0hlbGl0cm9uJwpuYW1lMCA9ICd0ZXwxMTY4MzU2NXwxMTY4OTgyMXwzfDYyNTd8K3xBVDNURTQ4NTQwfEFUQ09QSUE5NXxMVFIvQ29waWEnCm5hbWUwID0gZ3N1Yign4oiSJywgIi0iLCBuYW1lMCkKCgpuYW1lcy5hbGwgPSB1bmlxdWUoYyhyZXMubmVzdC5zaW0kVjFbcmVzLm5lc3Quc2ltJFY4ID09IG5hbWUwXSwKICAgICAgICAgICAgICAgICAgICAgcmVzLm5lc3Quc2ltJFY4W3Jlcy5uZXN0LnNpbSRWMSA9PSBuYW1lMF0pKQojIG5hbWVzLmFsbCA9IHVuaXF1ZShjKHJlcy5uZXN0JFYxW3Jlcy5uZXN0JFY4ID09IG5hbWUwXSwgCiMgICAgICAgICAgICAgICAgICAgICAgcmVzLm5lc3QkVjhbcmVzLm5lc3QkVjEgPT0gbmFtZTBdKSkKCnAuYWxsID0gbGlzdCgpCmZvcihuYW1lMiBpbiBuYW1lcy5hbGwpewogICMgbWVzc2FnZShuYW1lMikKICBzZXExID0gdGUuZmFzdGFbW25hbWUwXV0KICBzZXEyID0gdGUuZmFzdGFbW25hbWUyXV0KICAKICBzMSA9IHBhc3RlMChzdHJzcGxpdChuYW1lMCwgJ1xcfCcpW1sxXV1bNzo5XSwgY29sbGFwc2UgPSAnfCcpCiAgczIgPSBwYXN0ZTAoc3Ryc3BsaXQobmFtZTIsICdcXHwnKVtbMV1dWzc6OV0sIGNvbGxhcHNlID0gJ3wnKQogIHAgPSBkb3RwbG90KHNlcTEsIHNlcTIsIHdzaXplLCBubWF0Y2gpICsgeGxhYihzMSkgKyB5bGFiKHMyKQogIHAuYWxsW1tuYW1lMl1dID0gcAp9CgoKczAgPSBwYXN0ZTAoc3Ryc3BsaXQobmFtZTAsICdcXHwnKVtbMV1dWzc6OV0sIGNvbGxhcHNlID0gJ18nKQpzMCA9IGdzdWIoIi8iLCAiLSIsIHMwKQpwZGYocGFzdGUocGF0aC5maWd1cmVzLCAncGFpcndpc2VfY29ubmVjdF8nLHMwLCcucGRmJywgc2VwID0gJycpLCB3aWR0aCA9IDUwLCBoZWlnaHQgPSA1MCkKZ3JpZC5hcnJhbmdlKGdyb2JzID0gcC5hbGwsIG5jb2wgPSBjZWlsaW5nKHNxcnQobGVuZ3RoKHAuYWxsKSkpKSAjIFdyaXRlIHRoZSBncmlkLmFycmFuZ2UgaW4gdGhlIGZpbGUKZGV2Lm9mZigpICMgQ2xvc2UgdGhlIGZpbGUKCmBgYAoKCgpgYGB7cn0KCgpuYW1lMSA9ICd0ZXwxNDE4OTI1NnwxNDE5MDI2Nnw1fDEwMTF8LXxBVDVURTUwNzAwfEhFTElUUk9OWTN8UkMvSGVsaXRyb24nCm5hbWUyID0gJ3RlfDIxNjIyOTV8MjE2MjkzN3wyfDY0M3wtfEFUMlRFMDk5NTB8SEVMSVRST05ZM3xSQy9IZWxpdHJvbicKbmFtZTAgPSBuYW1lMQoKbmFtZXMuYWxsID0gdW5pcXVlKGMocmVzLm5lc3QkVjFbcmVzLm5lc3QkVjggPT0gbmFtZTBdLCByZXMubmVzdCRWOFtyZXMubmVzdCRWMSA9PSBuYW1lMF0pKQoKCm5hbWVzID0gYyhuYW1lMSwgbmFtZTIpCmIudG1wID0gYmwucmVzWyhibC5yZXMkVjEgJWluJSBuYW1lcykgJiAoYmwucmVzJFY4ICVpbiUgbmFtZXMpLF0KCnJlcy5uZXN0WyhyZXMubmVzdCRWMSAlaW4lIG5hbWVzKSAmIChyZXMubmVzdCRWOCAlaW4lIG5hbWVzKSwgXQoKYGBgCgoKIyBTVnMKIyMgUmVhZGluZ3Mgc2VTVnMKYGBge3J9Cgpzdi5zZSA9IHJlYWRSRFMocGFzdGUocGF0aC5zdnMsICdzdl9zZS5yZHMnLCBzZXAgPSAnJykpCgojIFJlbmFtZSBsZW5ndGggZ3JvdXBzCmxldi5yZXBsYWNlID0gYygnWzEsMTBdJywgJygxMCwxNV0nKQpsZXYubmV3ID0gJ1sxLDE1XScKCnMubGV2ZWxzID0gYXMuY2hhcmFjdGVyKGxldmVscyhzdi5zZSRsZW4uZ3IpKQpzLmxldmVscyA9IHMubGV2ZWxzWyEocy5sZXZlbHMgJWluJSBsZXYucmVwbGFjZSldCnMubGV2ZWxzID0gYyhsZXYubmV3LCBzLmxldmVscykKcy5sZXZlbHMgPSBnc3ViKCJlXFwrMDMiLCAiayIsIHMubGV2ZWxzKQoKc3Yuc2UkbGVuLmdyID0gYXMuY2hhcmFjdGVyKHN2LnNlJGxlbi5ncikKc3Yuc2UkbGVuLmdyW3N2LnNlJGxlbi5nciAlaW4lIGxldi5yZXBsYWNlXSA9IGxldi5uZXcKc3Yuc2UkbGVuLmdyID0gZ3N1YigiZVxcKzAzIiwgImsiLCBzdi5zZSRsZW4uZ3IpCnN2LnNlJGxlbi5nciA9IGZhY3Rvcihzdi5zZSRsZW4uZ3IsIGxldmVscyA9IHMubGV2ZWxzKQoKCiMgUmVwbGFjZSBmYW1pbGllcwpzdi5zZSRmYW0gPSBhcy5jaGFyYWN0ZXIoc3Yuc2UkZmFtKQpzdi5zZSRmYW0gPC0gZ3N1YigiSGVsaXRyb24vLioiLCAiTWl4IHdpdGggSGVsaXRyb24iLCBzdi5zZSRmYW0pCgoKc3Yuc2UkdGUgPSBmYWN0b3Ioc3Yuc2UkdGUsIGxldmVscyA9IGMoJ2lzVEUnLCAnaXNURXBhcnQnLCAnaGFzVEUnLCAnaGFzVEVwYXJ0JywgJ25vVEUnKSkKCgoKYGBgCgojIyBSZWFkIFNWIGZhc3RhCmBgYHtyfQoKc3Yuc2VxcyA9IHNlcWlucjo6cmVhZC5mYXN0YShwYXN0ZShwYXRoLnN2cywgJ3N2X3Bhbmdlbl9zZXFfc3ZfYmlnLmZhc3RhJywgc2VwID0gJycpKQoKYGBgCgoKIyMgUmVhZGluZyBuZXN0ZWRuZXNzCmBgYHtyfQoKIyBMb2FkIHNpbWlsYXJpdHkgZnVuY3Rpb24KCmZpbGUubmVzdGVkbmVzcyA9IHBhc3RlKHBhdGgud29yaywgJ3N2X2JpZ19vbl9iaWdfbmVzdC5yZHMnLCBzZXAgPSAnJykKCgppZighZmlsZS5leGlzdHMoZmlsZS5uZXN0ZWRuZXNzKSl7CiAgYmwuZmlsZSA9IHBhc3RlKHBhdGgud29yaywgJ3N2X2JpZ19vbl9iaWcudHh0Jywgc2VwID0gJycpCiAgYmwucmVzID0gcmVhZC50YWJsZShibC5maWxlKQogIGJsLnJlcyA9IGJsLnJlc1tibC5yZXMkVjEgIT0gYmwucmVzJFY4LF0KICAKICBibC5yZXMgPSBibC5yZXNbYmwucmVzJFY2ID49IHNpbS5jdXRvZmYgKiAxMDAsXQoKICByZXMubmVzdCA9IGZpbmROZXN0ZWRuZXNzKGJsLnJlcywgdXNlLnN0cmFuZCA9IEYpCiAgICAKICByZXMubmVzdCRsZW4xID0gcmVzLm5lc3QubGVuW3Jlcy5uZXN0JFYxXQogIHJlcy5uZXN0JGxlbjggPSByZXMubmVzdC5sZW5bcmVzLm5lc3QkVjhdCiAgcmVzLm5lc3QkcDEgPSByZXMubmVzdCRDMSAvIHJlcy5uZXN0JGxlbjEKICByZXMubmVzdCRwOCA9IHJlcy5uZXN0JEM4IC8gcmVzLm5lc3QkbGVuOCAgCiAgc2F2ZVJEUyhyZXMubmVzdCwgZmlsZS5uZXN0ZWRuZXNzLCBjb21wcmVzcyA9IEYpCn0gZWxzZSB7CiAgcmVzLm5lc3QgPSByZWFkUkRTKGZpbGUubmVzdGVkbmVzcykKfQoKcmVzLm5lc3QubGVuID0gc2FwcGx5KHVuaXF1ZShjKHJlcy5uZXN0JFYxLCByZXMubmVzdCRWOCkpLCAKICAgICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uKHMpIGFzLm51bWVyaWMoc3Ryc3BsaXQocywgJ1xcfCcpW1sxXV1bMl0pKQpyZXMubmVzdDAgPSByZXMubmVzdAoKCiMgRm9yIGZ1cnRoZXIgYW5hbHlzaXMgb2YgZXhhbXBsZXM6CmJsLmZpbGUgPSBwYXN0ZShwYXRoLndvcmssICdzdl9iaWdfb25fYmlnLnR4dCcsIHNlcCA9ICcnKQpibC5yZXMgPSByZWFkLnRhYmxlKGJsLmZpbGUpCmJsLnJlcyA9IGJsLnJlc1tibC5yZXMkVjEgIT0gYmwucmVzJFY4LF0KCmBgYAoKCiMjIFRFIHN0YXQKIyMjIEluIGdyYXBoIC0gbm90IGluIGdyYXBoCmBgYHtyfQpyZXMubmVzdCA9IHJlcy5uZXN0MAoKc3Yuc2UubGVuID0gc3Yuc2Vbc3Yuc2UkbGVuID49IDEwMCxdCnN2LnNlLmxlbiRpbi5jb25uZWN0ID0gc3Yuc2UubGVuJG5hbWUgJWluJSBuYW1lcyhyZXMubmVzdC5sZW4pCgpjbnQuc3Yuc2UgPSB0YWJsZShzdi5zZS5sZW4kaW4uY29ubmVjdCAsIHN2LnNlLmxlbiR0ZSkKY250LnN2LnNlCgpkZiA9IHJlc2hhcGUyOjptZWx0KGNudC5zdi5zZSkKCgoKZGYkVmFyMiA9IGZhY3RvcihkZiRWYXIyLCBsZXZlbHMgPSByZXYoYygnaXNURScsICdpc1RFcGFydCcsICdoYXNURScsICdoYXNURXBhcnQnLCAnbm9URScpKSkKCgojIGluc3RhbGwucGFja2FnZXMoImdncGF0dGVybiIpCgoKcCA9IGdncGxvdChkZiwgYWVzKHggPSBWYXIyLCB5ID0gdmFsdWUsIGZpbGwgPSBWYXIyLCBhbHBoYSA9IFZhcjEsIGNvbG9yID0gVmFyMSkpICsKICBnZW9tX2NvbF9wYXR0ZXJuKCBhZXMocGF0dGVybiA9IFZhcjEpLAogICAgIyBwYXR0ZXJuID0gcmVwKGMoJ25vbmUnLCAic3RyaXBlIiksIDUpLAogICAgcGF0dGVybl9kZW5zaXR5ID0gMC4xLAogICAgcGF0dGVybl9zcGFjaW5nID0gMC4wMjUsCiAgICBwYXR0ZXJuX2ZpbGwgPSAiZ3JleTcwIiwgCiAgICBwb3NpdGlvbiA9ICJkb2RnZSIsIAogICAgd2lkdGggPSAwLjgKICApICsgCiAgIyBnZW9tX2NvbChwb3NpdGlvbiA9ICJkb2RnZSIsIHdpZHRoID0gMC44KSArCiAgc2NhbGVfYWxwaGFfbWFudWFsKHZhbHVlcyA9IGMoMC44LCAxKSwgbGFiZWxzID0gYygiTm8iLCAiWWVzIikpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygnYmxhY2snLCAnYmxhY2snKSwgbGFiZWxzID0gYygibm90IGluIGdyYXBoIiwgImluIGdyYXBoIikpICsKICBzY2FsZV9wYXR0ZXJuX21hbnVhbCh2YWx1ZXMgPSBjKCJzdHJpcGUiLCAnbm9uZScpLCBsYWJlbHMgPSBjKCJpbiBncmFwaCIsICJub3QgaW4gZ3JhcGgiKSwKICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBjKFRSVUUsIEZBTFNFKSkgKwogIGxhYnMoZmlsbCA9ICIiLCBwYXR0ZXJuPSdDb25uZWN0ZWQgdG8gb3RoZXJzJykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHRlLmNvbHMpICsKICB4bGFiKE5VTEwpICsKICB5bGFiKCJOdW1iZXIgb2YgU1ZzIikgKwogIHRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpKSArIAogIGd1aWRlcyhhbHBoYSA9ICJub25lIiwgZmlsbCA9ICdub25lJywgY29sb3IgPSAnbm9uZScpICsKICB0aGVtZV9taW5pbWFsKCkgKyBjb29yZF9mbGlwKCkgKwogIHRoZW1lKAogICAgbGVnZW5kLnBvc2l0aW9uID0gYygwLjcsIDAuMyksICAgICAjIEFkanVzdCB0aGVzZSBjb29yZGluYXRlcyBhcyBuZWVkZWQKICAgIGxlZ2VuZC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGw9InRyYW5zcGFyZW50IiwgY29sb3I9J2dyZXk3MCcpICAKICApICsKICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIGd1aWRlcyhwYXR0ZXJuID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3QoZmlsbCA9IGMoIndoaXRlIiksIGNvbG9yPSAnYmxhY2snKSkpICAKcAoKcGRmKHBhc3RlKHBhdGguZmlndXJlcywgJ2dyYXBoX21vYl9pbl9ncmFwaC5wZGYnLCBzZXAgPSAnJyksIHdpZHRoID0gMywgaGVpZ2h0ID0gNCkKcHJpbnQocCkgICAgICMgUGxvdCAxIC0tPiBpbiB0aGUgZmlyc3QgcGFnZSBvZiBQREYKZGV2Lm9mZigpCgpgYGAKCgoKIyMjIFRFIGZhbWlsaWVzIGluIFNWIHR5cGVzCmBgYHtyfQoKc3Yuc2UubGVuID0gc3Yuc2Vbc3Yuc2UkbGVuID49IDEwMCxdCmNudC5mYW0uc3YgPSB0YWJsZShzdi5zZS5sZW4kZmFtW3N2LnNlLmxlbiRmYW0hPScnXSwgc3Yuc2UubGVuJHRlW3N2LnNlLmxlbiRmYW0hPScnXSkKY250LmZhbS5zdiA9IHQoYXBwbHkoY250LmZhbS5zdiwgMSwgZnVuY3Rpb24oeCkgeC9zdW0oeCkpKQpjbnQuZmFtLnN2ID0gY250LmZhbS5zdlssIGNvbFN1bXMoY250LmZhbS5zdikgIT0gMF0KY250LmZhbS5zdiA9IHJlc2hhcGUyOjptZWx0KGNudC5mYW0uc3YpCgpwID0gZ2dwbG90KGNudC5mYW0uc3YsIGFlcyh4ID0gVmFyMiwgeSA9IFZhcjEsIGNvbG9yID0gVmFyMikpICsgCiAgZ2VvbV9wb2ludChhZXMoc2l6ZSA9IHZhbHVlLCBhbHBoYSA9IHZhbHVlICogMikpICsgdGhlbWVfbWluaW1hbCgpICsgCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IHRlLmNvbHMpICArCiAgZ2VvbV90ZXh0KGRhdGEgPSBjbnQuZmFtLnN2W2NudC5mYW0uc3YkdmFsdWUgPj0gMC4yLF0sIAogICAgICAgICAgICAgIGFlcyh4PVZhcjIsIHk9VmFyMSwgbGFiZWwgPSByb3VuZCh2YWx1ZSwgMikpLCAKICAgICAgICAgICAgICBzaXplID0gMi41LCBjb2xvciA9ICdibGFjaycsIAogICAgICAgICAgICBudWRnZV94ID0gMC4zLAogICAgICAgICAgICBudWRnZV95ID0gMCkgKwogIGd1aWRlcyhzaXplID0gIm5vbmUiLCBhbHBoYSA9ICJub25lIiwgY29sb3IgPSAnbm9uZScpICsKICB4bGFiKCdTViB0eXBlJykgKyB5bGFiKCdURSBmYW1pbHknKQpwCgoKY250LmZhbS5zdiA9IHJvd1N1bXModGFibGUoc3Yuc2UubGVuJGZhbVtzdi5zZS5sZW4kZmFtIT0nJ10sIHN2LnNlLmxlbiR0ZVtzdi5zZS5sZW4kZmFtIT0nJ10pKQpjbnQuZmFtLnN2ID0gZGF0YS5mcmFtZSh2YWx1ZSA9IGNudC5mYW0uc3YsIG5hbWVzID0gbmFtZXMoY250LmZhbS5zdikpCnJvd25hbWVzKGNudC5mYW0uc3YpID0gTlVMTAoKZyA9IGdncGxvdChjbnQuZmFtLnN2LCBhZXMoeCA9IG5hbWVzLCB5ID0gdmFsdWUpKSArCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiLCBmaWxsPSJncmV5ODAiKSsKICBjb29yZF9mbGlwKCkgKyB0aGVtZV9taW5pbWFsKCkgKyB0aGVtZShheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICAgICAgICBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCkpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gcGFzdGUoIjFlIixzZXEoMCw0LDEpLCBzZXAgPSAnJyksIGJyZWFrcz0gc2VxKDAsNCwxKSoxMDAwKSArCiAgeWxhYignIycpICsgZ2VvbV90ZXh0KGFlcyhsYWJlbD12YWx1ZSwgeT0wKSwgaGp1c3Q9MCwgc2l6ZSA9IDIuNSkKZyAKCgpwcCA9IGdncHVicjo6Z2dhcnJhbmdlKHAgKyB4bGFiKCdURSBjb250ZW50JykgKyBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscyA9IGMoJ2lzIGNvbXBsLicsICdpcyBmcmFnbS4nLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdjb250LiBjb21wbC4nLCAnY29udC4gZnJhZ20uJykpICwgZywgbmNvbCA9IDIsIHdpZHRocyA9IGMoMC43NSwgMC4yNSkpCnBwCgpwZGYocGFzdGUocGF0aC5maWd1cmVzLCAnZ3JhcGhfbW9iX3RlX2ZhbV9zdl90eXBlLnBkZicsIHNlcCA9ICcnKSwgd2lkdGggPSA2LCBoZWlnaHQgPSA0KQpwcmludChwcCkgICAgICMgUGxvdCAxIC0tPiBpbiB0aGUgZmlyc3QgcGFnZSBvZiBQREYKZGV2Lm9mZigpCgoKIyBJbnNlcnRpb24gYW5kIGRlbGV0aW9uCmlkeCA9IChzdi5zZS5sZW4kZmFtIT0nJykgJiAoc3Yuc2UubGVuJGZyZXEubWF4IDw9IDMpCnRhYmxlKHN2LnNlLmxlbiRmYW1baWR4XSwgc3Yuc2UubGVuJHRlW2lkeF0pCgppZHggPSAoc3Yuc2UubGVuJGZhbSE9JycpICYgKHN2LnNlLmxlbiRmcmVxLm1heCA+PSAyNSkgJiAoc3Yuc2UubGVuJGxlbiA+PSAxMDApIAp0YWJsZShzdi5zZS5sZW4kZmFtW2lkeF0sIHN2LnNlLmxlbiR0ZVtpZHhdKQpgYGAKIyMjIFRFIGZhbTogVEFJUjEwCmBgYHtyfQpmLnRlLnJlZiA9IHBhc3RlKHBhdGgudGFpciwgJ25ld190ZS5mYXN0YScsIHNlcCA9ICcnKQpsaW5lcyA9IHJlYWRMaW5lcyhmLnRlLnJlZikKbGluZXMgPSBncmVwKCdePicsIGxpbmVzLCB2YWx1ZSA9IFQpCgpyZWYuZmFtID0gc2FwcGx5KGxpbmVzLCBmdW5jdGlvbih4KSBzdHJzcGxpdCh4LCAnXFx8JylbWzFdXVs5XSkKCgppbmRpY2VzIDwtIGdyZXAoIl5ETkEoPyEvSEFUfC9NdURSKSIsIHJlZi5mYW0sIHZhbHVlID0gRkFMU0UsIHBlcmwgPSBUUlVFKQpyZWYuZmFtW2luZGljZXNdID0gJ0ROQSsnCgppbmRpY2VzIDwtIGdyZXAoIl5SYXRoRSIsIHJlZi5mYW0sIHZhbHVlID0gRkFMU0UsIHBlcmwgPSBUUlVFKQpyZWYuZmFtW2luZGljZXNdID0gJ1JhdGhFMS8yLzNfY29ucycKCmluZGljZXMgPC0gZ3JlcCgiXkxJTkUiLCByZWYuZmFtLCB2YWx1ZSA9IEZBTFNFLCBwZXJsID0gVFJVRSkKcmVmLmZhbVtpbmRpY2VzXSA9ICdMSU5FJwpyZWYuZmFtW3JlZi5mYW0gPT0gJ1JDL0hlbGl0cm9uJ10gPSAnSGVsaXRyb24nCgpyZWYuZmFtLmNudCA9IHRhYmxlKHJlZi5mYW0pCgoKCmRmID0gY250LmZhbS5zdgpkZiRyZWYgPSBhcy5udW1lcmljKHJlZi5mYW0uY250W2RmJG5hbWVzXSkKZGYgPSBkZlshaXMubmEoZGYkcmVmKSxdCgpwbG90KGRmJHZhbHVlLCBkZiRyZWYpCgoKCnAgPC0gZ2dwbG90KGRmLCBhZXMoeCA9IHJlZiwgeSA9IHZhbHVlLCBjb2xvciA9IG5hbWVzKSkgKwogIGdlb21fc21vb3RoKGFlcyhncm91cCA9IDEpLCBtZXRob2QgPSAibG0iLCBmb3JtdWxhID0geSB+IHgsIHNlID0gRkFMU0UsIGNvbG9yID0gJ2dyZXk3MCcpICsgCiAgZ2VvbV9wb2ludCgpICsKICBnZ3JlcGVsOjpnZW9tX3RleHRfcmVwZWwoYWVzKGxhYmVsID0gbmFtZXMpLCBtYXgub3ZlcmxhcHMgPSAyMCkgKwogICMgeGxhYigibG9nICMgaW4gVEFJUjEwIGFubm90YXRpb24iKSArCiAgIyB5bGFiKCJsb2cgIyBpbiBTVnMiKSArCiAgIyBzY2FsZV94X2xvZzEwKCkgKwogICMgc2NhbGVfeV9sb2cxMCgpICsKICB4bGFiKCIjIGluIFRBSVIxMCBhbm5vdGF0aW9uIikgKwogIHlsYWIoIiMgaW4gU1ZzIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogIGd1aWRlcyhjb2xvciA9IEZBTFNFKSArCiAgdGhlbWVfbWluaW1hbCgpCnAKCgpsbV9tb2RlbCA8LSBsbSh2YWx1ZSB+IHJlZiwgZGF0YSA9IGRmKQpzbG9wZSA8LSBjb2VmKGxtX21vZGVsKVsyXQoKCnAgPSBwICsgYW5ub3RhdGUoInRleHQiLCB4ID0gbWluKGRmJHJlZiksIHkgPSBtYXgoZGYkdmFsdWUpLCAKICAgICAgICAgICBsYWJlbCA9IHBhc3RlKCdTbG9wZTonLCByb3VuZChzbG9wZSwgMykpLCBoanVzdCA9IDAsIHZqdXN0ID0gMSkKCgoKcGRmKHBhc3RlKHBhdGguZmlndXJlcywgJ2dyYXBoX21vYl90ZV9mYW1fdGFpcjEwLnBkZicsIHNlcCA9ICcnKSwgd2lkdGggPSA0LCBoZWlnaHQgPSA0KQpwcmludChwKSAgICAgIyBQbG90IDEgLS0+IGluIHRoZSBmaXJzdCBwYWdlIG9mIFBERgpkZXYub2ZmKCkKCgpgYGAKCgojIyBHcmFwaAojIyMgRmlsdHJhdGlvbgpgYGB7cn0KCnJlcy5uZXN0ID0gcmVzLm5lc3QwCgpzdi5uYW1lcy5taXggPSBzdi5zZSRuYW1lW2dyZXAoIl5NaXgiLCBzdi5zZSRmYW0pXQpyZXMubmVzdCA9IHJlcy5uZXN0WyEocmVzLm5lc3QkVjEgJWluJSBzdi5uYW1lcy5taXgpLF0KcmVzLm5lc3QgPSByZXMubmVzdFshKHJlcy5uZXN0JFY4ICVpbiUgc3YubmFtZXMubWl4KSxdCgoKc3YubmFtZXMubWl4ID0gc3Yuc2UkbmFtZVtzdi5zZSR0ZSA9PSAnbm9URSddCnJlcy5uZXN0ID0gcmVzLm5lc3RbIShyZXMubmVzdCRWMSAlaW4lIHN2Lm5hbWVzLm1peCksXQpyZXMubmVzdCA9IHJlcy5uZXN0WyEocmVzLm5lc3QkVjggJWluJSBzdi5uYW1lcy5taXgpLF0KCnNpbmdsZXRvbi5tb2RlID0gRgppZihzaW5nbGV0b24ubW9kZSl7CiAgc3YubmFtZXMuZnJlcSA9IHN2LnNlJG5hbWVbc3Yuc2UkZnJlcS5tYXggPD0gM10KICAjIHN2Lm5hbWVzLmZyZXEgPSBzdi5zZSRuYW1lW3N2LnNlJGZyZXEubWF4ID49IDI1XQogIHJlcy5uZXN0ID0gcmVzLm5lc3RbcmVzLm5lc3QkVjEgJWluJSBzdi5uYW1lcy5mcmVxLF0KICByZXMubmVzdCA9IHJlcy5uZXN0W3Jlcy5uZXN0JFY4ICVpbiUgc3YubmFtZXMuZnJlcSxdCn0KCnByZWZpeC5tb2RlID0gYygnJywgJ19zaW5nbGUnKQoKCmBgYAoKCiMjIyBDb25zdHJ1Y3QKYGBge3J9CiMgYWxsIGVkZ2VzCmlkeCA9IHJlcy5uZXN0JHAxID49IHNpbS5jdXRvZmYKZWRnZXMgPSBjYmluZChyZXMubmVzdCRWMVtpZHhdLCByZXMubmVzdCRWOFtpZHhdKQppZHggPSByZXMubmVzdCRwOCA+PSBzaW0uY3V0b2ZmCmVkZ2VzID0gcmJpbmQoZWRnZXMsIGNiaW5kKHJlcy5uZXN0JFY4W2lkeF0sIHJlcy5uZXN0JFYxW2lkeF0pKQp0ZS5lbmdlcy5uYW1lcyA9IHVuaXF1ZShjKGVkZ2VzWywxXSwgZWRnZXNbLDJdKSkKCnRtcCA9IHN2LnNlJHRlCm5hbWVzKHRtcCkgPSBzdi5zZSRuYW1lCnRlLmVuZ2VzLnR5cGUgPSBhcy5jaGFyYWN0ZXIodG1wW3RlLmVuZ2VzLm5hbWVzXSkKbmFtZXModGUuZW5nZXMudHlwZSkgPC0gbmFtZXModG1wW3RlLmVuZ2VzLm5hbWVzXSkKCgp0bXAgPSBzdi5zZSRmYW0KbmFtZXModG1wKSA9IHN2LnNlJG5hbWUKdGUuZW5nZXMuZmFtID0gdG1wW3RlLmVuZ2VzLm5hbWVzXQoKIyBub2RlcwppZHggPSAocmVzLm5lc3QkcDEgPj0gc2ltLmN1dG9mZikgJiAocmVzLm5lc3QkcDggPj0gc2ltLmN1dG9mZikKdGUubm9kZXMgPSBjYmluZChyZXMubmVzdCRWMVtpZHhdLCByZXMubmVzdCRWOFtpZHhdKQp0ZS5yZXN0ID0gc2V0ZGlmZih0ZS5lbmdlcy5uYW1lcywgYyh0ZS5ub2Rlc1ssMV0sIHRlLm5vZGVzWywyXSkpCgoKdGUubm9kZXMuZ3JhcGggPC0gaWdyYXBoOjptYWtlX2dyYXBoKHQodGUubm9kZXMpLCBkaXJlY3RlZCA9IFQpCnRlLm5vZGVzLmdyYXBoIDwtIGlncmFwaDo6c2ltcGxpZnkodGUubm9kZXMuZ3JhcGgpCnRlLm5vZGVzLmNvbXAgPC0gaWdyYXBoOjpjb21wb25lbnRzKHRlLm5vZGVzLmdyYXBoKQoKbm9kZXMgPSBkYXRhLmZyYW1lKG5vZGUgPSBwYXN0ZSgnTicsIHRlLm5vZGVzLmNvbXAkbWVtYmVyc2hpcCwgc2VwID0gJycpLCB0ZSA9IG5hbWVzKHRlLm5vZGVzLmNvbXAkbWVtYmVyc2hpcCkpCgpub2Rlcy5yZXN0ID0gZGF0YS5mcmFtZShub2RlID0gcGFzdGUoJ1InLCAoMTpsZW5ndGgodGUucmVzdCkpLCBzZXAgPSAnJyksIHRlID0gdGUucmVzdCkKbm9kZXMgPSByYmluZChub2Rlcywgbm9kZXMucmVzdCkKCnJvd25hbWVzKG5vZGVzKSA9IG5vZGVzJHRlCgojIERlZmluZSBURSB0eXBlCm5vZGVzLmNudCA9IGRhdGEuZnJhbWUoY250ID0gYyh0YWJsZShub2RlcyRub2RlKSkpCm5vZGVzLmNudCRub2RlID0gcm93bmFtZXMobm9kZXMuY250KQpub2Rlcy5jbnQkdHlwZSA9IHNhcHBseShub2Rlcy5jbnQkbm9kZSwgZnVuY3Rpb24ocyl7CiAgcy50ZSA9IG5vZGVzJHRlW25vZGVzJG5vZGUgPT0gc10KICB0eXBlLnRlID0gdW5pcXVlKHRlLmVuZ2VzLnR5cGVbcy50ZV0pCiAgaWYobGVuZ3RoKHR5cGUudGUpID09IDEpewogICAgcmV0dXJuKHR5cGUudGUpCiAgfSBlbHNlIHsKICAgIHR5cGUudGUgPSB0YWJsZSh0eXBlLnRlKQogICAgdHlwZS50ZSA9IG5hbWVzKHR5cGUudGUpW3R5cGUudGUgPT0gbWF4KHR5cGUudGUpXQogICAgcmV0dXJuKHR5cGUudGVbMV0pCiAgfQp9KQp0YWJsZShub2Rlcy5jbnQkdHlwZSkKCiMgRGVmaW5lIFRFIGZhbWlseQpub2Rlcy5jbnQkZmFtID0gc2FwcGx5KG5vZGVzLmNudCRub2RlLCBmdW5jdGlvbihzKXsKICBzLnRlID0gbm9kZXMkdGVbbm9kZXMkbm9kZSA9PSBzXQogIHR5cGUudGUgPSB1bmlxdWUodGUuZW5nZXMuZmFtW3MudGVdKQogIGlmKGxlbmd0aCh0eXBlLnRlKSA9PSAxKXsKICAgIHJldHVybih0eXBlLnRlKQogIH0gZWxzZSB7CiAgICB0eXBlLnRlID0gdGFibGUodHlwZS50ZSkKICAgIHR5cGUudGUgPSBuYW1lcyh0eXBlLnRlKVt0eXBlLnRlID09IG1heCh0eXBlLnRlKV0KICAgIHJldHVybih0eXBlLnRlWzFdKQogIH0KfSkKdGFibGUobm9kZXMuY250JGZhbSkKCgojIFJlZGVmaW5lIGVkZ2VzIGJ1dCB3aXRoIG5vZGUgbmFtZXMKaWR4LmVuZGVzID0gKGVkZ2VzWywxXSAlaW4lIG5vZGVzJHRlKSAmIChlZGdlc1ssMl0gJWluJSBub2RlcyR0ZSkKYi5ncmFwaCA9IGNiaW5kKG5vZGVzW2VkZ2VzW2lkeC5lbmRlcywxXSwgJ25vZGUnXSxub2Rlc1tlZGdlc1tpZHguZW5kZXMsMl0sICdub2RlJ10pCmIuZ3JhcGggPSB1bmlxdWUoYi5ncmFwaCkKIyBiLmdyYXBoID0gYi5ncmFwaFtiLmdyYXBoWywxXSAhPSBiLmdyYXBoWywyXSxdCmIuZ3JhcGgudW5pID0gYi5ncmFwaFtiLmdyYXBoWywxXSA9PSBiLmdyYXBoWywyXSxdCmIuZ3JhcGggPSBiLmdyYXBoW2IuZ3JhcGhbLDFdICE9IGIuZ3JhcGhbLDJdLF0KCmxlbmd0aCh1bmlxdWUoYyhiLmdyYXBoWywxXSwgYi5ncmFwaFssMl0pKSkKCiMgcmVkdWNlIGluZGlyZWN0IGFycm93cwppZHgucmVtb3ZlID0gYygpCmZvcihpLmVkZ2UgaW4gMTpucm93KGIuZ3JhcGgpKXsKICBpZihpLmVkZ2UgJSUgMTAwMCA9PSAwKSBwcmludChpLmVkZ2UpCiAgdG1wLnRvID0gYi5ncmFwaFtiLmdyYXBoWywxXSA9PSBiLmdyYXBoW2kuZWRnZSwxXSwyXQogIHRtcC5mcm9tID0gYi5ncmFwaFtiLmdyYXBoWywyXSA9PSBiLmdyYXBoW2kuZWRnZSwyXSwxXQogIGlmKGxlbmd0aChpbnRlcnNlY3QodG1wLnRvLCB0bXAuZnJvbSkpID4gMCkgaWR4LnJlbW92ZSA9IGMoaWR4LnJlbW92ZSwgaS5lZGdlKQp9CmlkeC5yZW1vdmUgPSB1bmlxdWUoaWR4LnJlbW92ZSkKYi5ncmFwaCA9IGIuZ3JhcGhbLWlkeC5yZW1vdmUsXQojIGIuZ3JhcGggPSByYmluZChiLmdyYXBoLCBiLmdyYXBoLnVuaSkKCiMgUHJpbnQgZ3JhcGgKCmcubm9kZXMudHlwZSA9IG5vZGVzLmNudCR0eXBlCm5hbWVzKGcubm9kZXMudHlwZSkgPSBub2Rlcy5jbnQkbm9kZQpnLm5vZGVzLmNudCA9IG5vZGVzLmNudCRjbnQKbmFtZXMoZy5ub2Rlcy5jbnQpID0gbm9kZXMuY250JG5vZGUKZy5ub2Rlcy5mYW0gPSBub2Rlcy5jbnQkZmFtCm5hbWVzKGcubm9kZXMuZmFtKSA9IG5vZGVzLmNudCRub2RlCgoKIyBnLmNvbHMubmFtZXMgPSBjKCJub1RFIiwgImlzVEUiLCAiaGFzVEUiLCAiaGFzVEVwYXJ0IiwgImlzVEVwYXJ0IikKIyBnLmNvbHMgPSBjKCcjRkZEOTY2JywgJyNFQjQ1NUYnLCAnIzdCNjA3OScsICcjM0M4REFEJywgJyM3OUI3NzMnKQojIG5hbWVzKGcuY29scykgPSBnLmNvbHMubmFtZXMKCgpnLnBhcnQgPC0gbmV0d29yayhiLmdyYXBoLCBtYXRyaXgudHlwZSA9ICJlZGdlbGlzdCIsIGlnbm9yZS5ldmFsID0gRkFMU0UsIGRpcmVjdGVkID0gVFJVRSkKYi5ncmFwaC5uYW1lcyA9IG5ldHdvcmsudmVydGV4Lm5hbWVzKGcucGFydCkKCnNldC5zZWVkKDIwKQpwIDwtIGdnbmV0MihnLnBhcnQsIGxhYmVsID0gRiwgZWRnZS5jb2xvciA9ICJibGFjayIsIAogICAgICAgICAgICBub2RlLnNpemUgPSBnLm5vZGVzLmNudFtiLmdyYXBoLm5hbWVzXSwgCiAgICAgICAgICAgIGNvbG9yID0gZy5ub2Rlcy50eXBlW2IuZ3JhcGgubmFtZXNdLAogICAgICAgICAgICAjIG1vZGUgPSAna2FtYWRha2F3YWknLAogICAgICAgICAgICAjIGFycm93LmdhcCA9IDAsIAogICAgICAgICAgICAjIGFycm93LnNpemUgPSAzLAogICAgICAgICAgICBwYWxldHRlID0gdGUuY29scykgKyBndWlkZXMoc2l6ZSA9IEYpCnAKCiMgcGF0aC5maWd1cmVzICA9ICcvVm9sdW1lcy9TYW1zdW5nX1Q1L3ZpZW5uL3dvcmtfdGUvJwpwZGYocGFzdGUocGF0aC5maWd1cmVzLCAnZ3JhcGhfbW9iX2FsbF9jbHVzdGVyJywgcHJlZml4Lm1vZGVbc2luZ2xldG9uLm1vZGUrMV0gLCdfdHlwZS5wZGYnLCBzZXAgPSAnJyksIAogICAgd2lkdGggPSA0LjYsIGhlaWdodCA9IDQuNikKcHJpbnQocCsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSkgICAgICMgUGxvdCAxIC0tPiBpbiB0aGUgZmlyc3QgcGFnZSBvZiBQREYKZGV2Lm9mZigpCgoKIyBzZXQuc2VlZCgyMCkKIyBwIDwtIGdnbmV0MihnLnBhcnQsIGxhYmVsID0gRiwgZWRnZS5jb2xvciA9ICJncmV5MzAiLCAKIyAgICAgICAgICAgICBub2RlLnNpemUgPSBnLm5vZGVzLmNudFtiLmdyYXBoLm5hbWVzXSwgCiMgICAgICAgICAgICAgY29sb3IgPSBjKCdURScsICdub1RFJylbKGcubm9kZXMudHlwZVtiLmdyYXBoLm5hbWVzXSA9PSAnbm9URScpKjErMV0sCiMgICAgICAgICAgICAgIyBtb2RlID0gJ2thbWFkYWthd2FpJywKIyAgICAgICAgICAgICAjIGFycm93LmdhcCA9IDAsIAojICAgICAgICAgICAgICMgYXJyb3cuc2l6ZSA9IDMsCiMgICAgICAgICAgICAgcGFsZXR0ZSA9IGMoJ25vVEUnID0gJ2JsYWNrJywgJ1RFJyA9ICcjQUVDM0FFJykpICsgZ3VpZGVzKHNpemUgPSBGKQojIHAKIyAKIyAjIHBhdGguZmlndXJlcyAgPSAnL1ZvbHVtZXMvU2Ftc3VuZ19UNS92aWVubi93b3JrX3RlLycKIyBwZGYocGFzdGUocGF0aC5maWd1cmVzLCAnZ3JhcGhfbW9iX2FsbF9jbHVzdGVyJywgcHJlZml4Lm1vZGVbc2luZ2xldG9uLm1vZGUrMV0gLCdfdHlwZS5wZGYnLCBzZXAgPSAnJyksIAojICAgICB3aWR0aCA9IDUsIGhlaWdodCA9IDUpCiMgcHJpbnQocCsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSkgICAgICMgUGxvdCAxIC0tPiBpbiB0aGUgZmlyc3QgcGFnZSBvZiBQREYKIyBkZXYub2ZmKCkKCmBgYAoKCiMjIyBDb2xvcmVkIGJ5IFRFIGZhbWlseQpgYGB7cn0KCmlmKGxlbmd0aChzZXRkaWZmKGcubm9kZXMuZmFtLCBuYW1lcyhmYW0ucGFsZXR0ZSkpKSE9MCkgc3RvcCgnbm90IGFsbCBmYW1pbGllcyBhcmUgZGVmaW5lZCcpCgpzZXQuc2VlZCgyMCkKcCA8LSBnZ25ldDIoZy5wYXJ0LCBsYWJlbCA9IEYsIGVkZ2UuY29sb3IgPSAiZ3JleTIwIiwgCiAgICAgICAgICAgIG5vZGUuc2l6ZSA9IGcubm9kZXMuY250W2IuZ3JhcGgubmFtZXNdLCAKICAgICAgICAgICAgY29sb3IgPSBnLm5vZGVzLmZhbVtiLmdyYXBoLm5hbWVzXSwKICAgICAgICAgICAgIyBtb2RlID0gJ2thbWFkYWthd2FpJywKICAgICAgICAgICAgIyBhcnJvdy5nYXAgPSAwLCAKICAgICAgICAgICAgIyBhcnJvdy5zaXplID0gMywKICAgICAgICAgICAgcGFsZXR0ZSA9IGZhbS5wYWxldHRlKSArIGd1aWRlcyhzaXplID0gRikKcCA9IHAgKyB0aGVtZShsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gOCksIAogICAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgbGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjUsICJjbSIpKSArIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZChuY29sID0gMikpCnAKCgpwZGYocGFzdGUocGF0aC5maWd1cmVzLCAnZ3JhcGhfbW9iX2FsbF9jbHVzdGVyJywgcHJlZml4Lm1vZGVbc2luZ2xldG9uLm1vZGUrMV0gLCdfZmFtaWx5LnBkZicsIHNlcCA9ICcnKSwgCiAgICB3aWR0aCA9IDQuNiwgaGVpZ2h0ID0gNC42KQpwcmludChwKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpKSAgICAgIyBQbG90IDEgLS0+IGluIHRoZSBmaXJzdCBwYWdlIG9mIFBERgpkZXYub2ZmKCkKCgpwZGYocGFzdGUocGF0aC5maWd1cmVzLCAnZ3JhcGhfbW9iX2FsbF9jbHVzdGVyJywgcHJlZml4Lm1vZGVbc2luZ2xldG9uLm1vZGUrMV0gLCdfZmFtaWx5X2xlZ2VuZC5wZGYnLCBzZXAgPSAnJyksIHdpZHRoID0gNywgaGVpZ2h0ID0gNSkKcHJpbnQocCsgY29vcmRfZml4ZWQocmF0aW8gPSAxKSkgICAgICMgUGxvdCAxIC0tPiBpbiB0aGUgZmlyc3QgcGFnZSBvZiBQREYKZGV2Lm9mZigpCgoKYGBgCgoKIyMjIE5vZGUgc2l6ZSBkaXN0cmlidXRpb24KYGBge3J9CmRmID0gZGF0YS5mcmFtZShub2RlID0gdW5pcXVlKG5vZGVzJG5vZGUpKQpkZiRzaXplID0gZy5ub2Rlcy5jbnRbZGYkbm9kZV0KZGYkZmFtID0gZy5ub2Rlcy5mYW1bZGYkbm9kZV0KZGYkdHlwZSA9IGcubm9kZXMudHlwZVtkZiRub2RlXQoKZmFtLnBhbGV0dGUKCnAgPSBnZ3Bsb3QoZGYsIGFlcyh4ID0gdHlwZSwgeSA9IHNpemUsIGNvbG9yPWZhbSkpICsKICBnZW9tX2ppdHRlcih3aWR0aCA9IDAuMikgKwogIGxhYnMoeCA9ICJUeXBlIiwgeSA9ICJTaXplIikgKyAKICBzY2FsZV95X2NvbnRpbnVvdXModHJhbnMgPSAibG9nMiIpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gZmFtLnBhbGV0dGUpKwogIHRoZW1lX21pbmltYWwoKSArCiAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKG5jb2wgPSAyKSkgKwogIGxhYnMoY29sb3IgPSAiVEUgZmFtaWx5IikgKyB4bGFiKCcnKSArIHlsYWIoJ05vZGUgc2l6ZSAoTnVtYmVyIG9mIHNpbWlsYXIgU1ZzKScpCnAKCgpwZGYocGFzdGUocGF0aC5maWd1cmVzLCAnZ3JhcGhfbW9iX3NpemVfZGlzdHJpYnV0aW9uLnBkZicsIHNlcCA9ICcnKSwgd2lkdGggPSA2LjUsIGhlaWdodCA9IDQpCnByaW50KHApICAgICAjIFBsb3QgMSAtLT4gaW4gdGhlIGZpcnN0IHBhZ2Ugb2YgUERGCmRldi5vZmYoKQoKYGBgCgoKCiMjIyBTZXBhcmF0ZWx5IHZpc3VhbGlzZSBjb25uZWN0ZWQgY29tcG9uZW50cwpgYGB7cn0KdG1wLmdyYXBoIDwtIGlncmFwaDo6bWFrZV9ncmFwaCh0KGIuZ3JhcGgpLCBkaXJlY3RlZCA9IFQpCnRtcC5ncmFwaCA8LSBpZ3JhcGg6OnNpbXBsaWZ5KHRtcC5ncmFwaCkKdG1wLmNvbXAgPC0gaWdyYXBoOjpjb21wb25lbnRzKHRtcC5ncmFwaCkKCnRtcC5jbnQgPSB0YWJsZSh0bXAuY29tcCRtZW1iZXJzaGlwKQp0bXAuY250ID0gLXNvcnQoLXRtcC5jbnQpCmhlYWQodG1wLmNudCkKCmsgPSAxCnRtcC5rID0gYXMubnVtZXJpYyhuYW1lcyh0bXAuY250KVtrXSkKdG1wLm5hbWVzID0gbmFtZXModG1wLmNvbXAkbWVtYmVyc2hpcClbdG1wLmNvbXAkbWVtYmVyc2hpcCA9PSB0bXAua10KYi5ncmFwaC5zdWIgPSBiLmdyYXBoWyhiLmdyYXBoWywxXSAlaW4lIHRtcC5uYW1lcykgJiAKICAgICAgICAgICAgICAgICAgICAgICAgKGIuZ3JhcGhbLDJdICVpbiUgdG1wLm5hbWVzKSxdCgpnLnBhcnQuc3ViLmJpZyA8LSBuZXR3b3JrKGIuZ3JhcGguc3ViLCBtYXRyaXgudHlwZSA9ICJlZGdlbGlzdCIsIGlnbm9yZS5ldmFsID0gRkFMU0UsIGRpcmVjdGVkID0gVFJVRSkKYi5ncmFwaC5uYW1lcy5zdWIuYmlnID0gbmV0d29yay52ZXJ0ZXgubmFtZXMoZy5wYXJ0LnN1Yi5iaWcpCgoKc2V0LnNlZWQoMjApCnAgPC0gZ2duZXQyKGcucGFydC5zdWIuYmlnLCBsYWJlbCA9IEYsIGVkZ2UuY29sb3IgPSAiYmxhY2siLCAKICAgICAgICAgICAgbm9kZS5zaXplID0gZy5ub2Rlcy5jbnRbYi5ncmFwaC5uYW1lcy5zdWIuYmlnXSwgCiAgICAgICAgICAgIGNvbG9yID0gZy5ub2Rlcy50eXBlW2IuZ3JhcGgubmFtZXMuc3ViLmJpZ10sCiAgICAgICAgICAgIG1vZGUgPSAna2FtYWRha2F3YWknLAogICAgICAgICAgICBwYWxldHRlID0gdGUuY29scykgKyBndWlkZXMoc2l6ZSA9IEYpCnAuYmlnLnR5cGUgPSBwICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKc2V0LnNlZWQoMjApCnAgPC0gZ2duZXQyKGcucGFydC5zdWIuYmlnLCBsYWJlbCA9IEYsIGVkZ2UuY29sb3IgPSAiYmxhY2siLCAKICAgICAgICAgICAgbm9kZS5zaXplID0gZy5ub2Rlcy5jbnRbYi5ncmFwaC5uYW1lcy5zdWIuYmlnXSwgCiAgICAgICAgICAgIGNvbG9yID0gZy5ub2Rlcy5mYW1bYi5ncmFwaC5uYW1lcy5zdWIuYmlnXSwKICAgICAgICAgICAgbW9kZSA9ICdrYW1hZGFrYXdhaScsCiAgICAgICAgICAgIHBhbGV0dGUgPSBmYW0ucGFsZXR0ZSkgKyBndWlkZXMoc2l6ZSA9IEYpCnAuYmlnLmNvbG9yID0gcCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCgp0bXAuayA9IGFzLm51bWVyaWMobmFtZXModG1wLmNudClba10pCnRtcC5uYW1lcyA9IG5hbWVzKHRtcC5jb21wJG1lbWJlcnNoaXApW3RtcC5jb21wJG1lbWJlcnNoaXAgIT0gdG1wLmtdCmIuZ3JhcGguc3ViID0gYi5ncmFwaFsoYi5ncmFwaFssMV0gJWluJSB0bXAubmFtZXMpICYgCiAgICAgICAgICAgICAgICAgICAgICAgIChiLmdyYXBoWywyXSAlaW4lIHRtcC5uYW1lcyksXQoKZy5wYXJ0LnN1Yi5zbWFsbCA8LSBuZXR3b3JrKGIuZ3JhcGguc3ViLCBtYXRyaXgudHlwZSA9ICJlZGdlbGlzdCIsIGlnbm9yZS5ldmFsID0gRkFMU0UsIGRpcmVjdGVkID0gVFJVRSkKYi5ncmFwaC5uYW1lcy5zdWIuc21hbGwgPSBuZXR3b3JrLnZlcnRleC5uYW1lcyhnLnBhcnQuc3ViLnNtYWxsKQoKCnNldC5zZWVkKDIwKQpwIDwtIGdnbmV0MihnLnBhcnQuc3ViLnNtYWxsLCBsYWJlbCA9IEYsIGVkZ2UuY29sb3IgPSAiYmxhY2siLCAKICAgICAgICAgICAgbm9kZS5zaXplID0gZy5ub2Rlcy5jbnRbYi5ncmFwaC5uYW1lcy5zdWIuc21hbGxdLCAKICAgICAgICAgICAgY29sb3IgPSBnLm5vZGVzLnR5cGVbYi5ncmFwaC5uYW1lcy5zdWIuc21hbGxdLAogICAgICAgICAgICAjIG1vZGUgPSAna2FtYWRha2F3YWknLAogICAgICAgICAgICBwYWxldHRlID0gdGUuY29scykgKyBndWlkZXMoc2l6ZSA9IEYpCnAuc21hbGwudHlwZSA9cCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCnNldC5zZWVkKDIwKQpwIDwtIGdnbmV0MihnLnBhcnQuc3ViLnNtYWxsLCBsYWJlbCA9IEYsIGVkZ2UuY29sb3IgPSAiYmxhY2siLCAKICAgICAgICAgICAgbm9kZS5zaXplID0gZy5ub2Rlcy5jbnRbYi5ncmFwaC5uYW1lcy5zdWIuc21hbGxdLCAKICAgICAgICAgICAgY29sb3IgPSBnLm5vZGVzLmZhbVtiLmdyYXBoLm5hbWVzLnN1Yi5zbWFsbF0sCiAgICAgICAgICAgICMgbW9kZSA9ICdrYW1hZGFrYXdhaScsCiAgICAgICAgICAgIHBhbGV0dGUgPSBmYW0ucGFsZXR0ZSkgKyBndWlkZXMoc2l6ZSA9IEYpCnAuc21hbGwuY29sb3IgPSBwICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKCgpgYGAKCiMjIyMgU2F2ZQpgYGB7cn0KIyBwLmJpZy50eXBlCiMgcC5iaWcuY29sb3IKIyBwLnNtYWxsLnR5cGUKIyBwLnNtYWxsLmNvbG9yCgpzaXplLnVuaXRzID0gNC42CgpwZGYocGFzdGUocGF0aC5maWd1cmVzLCAnZ3JhcGhfbW9iX2JpZ19jbHVzdGVyJywgcHJlZml4Lm1vZGVbc2luZ2xldG9uLm1vZGUrMV0gLCdfdHlwZS5wZGYnLCBzZXAgPSAnJyksIAogICAgd2lkdGggPSBzaXplLnVuaXRzLCBoZWlnaHQgPSBzaXplLnVuaXRzKQpwcmludChwLmJpZy50eXBlKSAgICAgIyBQbG90IDEgLS0+IGluIHRoZSBmaXJzdCBwYWdlIG9mIFBERgpkZXYub2ZmKCkKCnBkZihwYXN0ZShwYXRoLmZpZ3VyZXMsICdncmFwaF9tb2JfYmlnX2NsdXN0ZXInLCBwcmVmaXgubW9kZVtzaW5nbGV0b24ubW9kZSsxXSAsJ19mYW1pbHkucGRmJywgc2VwID0gJycpLCAKICAgIHdpZHRoID0gc2l6ZS51bml0cywgaGVpZ2h0ID0gc2l6ZS51bml0cykKcHJpbnQocC5iaWcuY29sb3IpICAgICAjIFBsb3QgMSAtLT4gaW4gdGhlIGZpcnN0IHBhZ2Ugb2YgUERGCmRldi5vZmYoKQoKcGRmKHBhc3RlKHBhdGguZmlndXJlcywgJ2dyYXBoX21vYl9zbWFsbF9jbHVzdGVyJywgcHJlZml4Lm1vZGVbc2luZ2xldG9uLm1vZGUrMV0gLCdfdHlwZS5wZGYnLCBzZXAgPSAnJyksIAogICAgd2lkdGggPSA2LCBoZWlnaHQgPSA2KQpwcmludChwLnNtYWxsLnR5cGUpICAgICAjIFBsb3QgMSAtLT4gaW4gdGhlIGZpcnN0IHBhZ2Ugb2YgUERGCmRldi5vZmYoKQoKcGRmKHBhc3RlKHBhdGguZmlndXJlcywgJ2dyYXBoX21vYl9zbWFsbF9jbHVzdGVyJywgcHJlZml4Lm1vZGVbc2luZ2xldG9uLm1vZGUrMV0gLCdfZmFtaWx5LnBkZicsIHNlcCA9ICcnKSwgCiAgICB3aWR0aCA9IDYsIGhlaWdodCA9IDYpCnByaW50KHAuc21hbGwuY29sb3IpICAgICAjIFBsb3QgMSAtLT4gaW4gdGhlIGZpcnN0IHBhZ2Ugb2YgUERGCmRldi5vZmYoKQoKCgpgYGAKCgojIyBFeGFtcGxlcyB3aXRoIGJpZyAiaGFzIFRFIgpgYGB7cn0KCnBhdGguY29tcG9uZW50cyA9IHBhc3RlKHBhdGguZmlndXJlcywgJ2NvbXBvbmVudHMvJywgc2VwID0gJycpCmlmICghZGlyLmV4aXN0cyhwYXRoLmNvbXBvbmVudHMpKSB7CiAgZGlyLmNyZWF0ZShwYXRoLmNvbXBvbmVudHMpCn0KCiMgRmluZCBhIGJpZyBub2RlLCB3aGlzaCBpcyAiSGF2ZSB0ZSIsIHdoaWNoIHdoaWNoIGlzIG5vdCBpbiB0aGUgYmlnZ2VzdCBjb25uZWN0ZWQgY29tcG9uZW50Cgpub2Rlcy5oYXZldGUuYmlnID0gbm9kZXMuY250JG5vZGVbKG5vZGVzLmNudCRjbnQgPj0gNykgJiAoKG5vZGVzLmNudCR0eXBlID09ICdoYXNURScpIHwgKG5vZGVzLmNudCR0eXBlID09ICdoYXNURXBhcnQnKSldCm5vZGVzLmhhdmV0ZS5iaWcgPSBub2Rlcy5oYXZldGUuYmlnW25vZGVzLmhhdmV0ZS5iaWcgJWluJSBuYW1lcyh0bXAuY29tcCRtZW1iZXJzaGlwKV0KCgpub2Rlcy50YXJnZXQgPSB1bmlxdWUobm9kZXMuaGF2ZXRlLmJpZ1t0bXAuY29tcCRtZW1iZXJzaGlwW25vZGVzLmhhdmV0ZS5iaWddICE9IGFzLm51bWVyaWMobmFtZXModG1wLmNudClbMV0pXSkKCiMgZm9yKGkudGFyZ2V0IGluIDE6bWluKDgwLCBsZW5ndGgobm9kZXMudGFyZ2V0KSkpewpmb3IoaS50YXJnZXQgaW4gYygxKSl7CiAgbWVzc2FnZShpLnRhcmdldCkKICAKICBjb21wLnRhcmdldCA9IGFzLm51bWVyaWModG1wLmNvbXAkbWVtYmVyc2hpcFtub2Rlcy50YXJnZXRbaS50YXJnZXRdXSkKICAKICAjIFZpc3VhbGlzZSBvbmUgY29tcG9uZW50CiAgCiAgdG1wLmsgPSBjb21wLnRhcmdldAogIHRtcC5uYW1lcyA9IG5hbWVzKHRtcC5jb21wJG1lbWJlcnNoaXApW3RtcC5jb21wJG1lbWJlcnNoaXAgPT0gdG1wLmtdCiAgYi5ncmFwaC5zdWIgPSBiLmdyYXBoWyhiLmdyYXBoWywxXSAlaW4lIHRtcC5uYW1lcykgfCAKICAgICAgICAgICAgICAgICAgICAgICAgICAoYi5ncmFwaFssMl0gJWluJSB0bXAubmFtZXMpLCxkcm9wPUZdCiAgCiAgZy5wYXJ0LnN1Yi5iaWcgPC0gbmV0d29yayhiLmdyYXBoLnN1YiwgbWF0cml4LnR5cGUgPSAiZWRnZWxpc3QiLCBpZ25vcmUuZXZhbCA9IEZBTFNFLCBkaXJlY3RlZCA9IFRSVUUpCiAgYi5ncmFwaC5uYW1lcy5zdWIuYmlnID0gbmV0d29yay52ZXJ0ZXgubmFtZXMoZy5wYXJ0LnN1Yi5iaWcpCiAgCiAgCiAgc2V0LnNlZWQoMjApCiAgcC50ZSA8LSBnZ25ldDIoZy5wYXJ0LnN1Yi5iaWcsIGxhYmVsID0gRiwgZWRnZS5jb2xvciA9ICJibGFjayIsIAogICAgICAgICAgICAgIG5vZGUuc2l6ZSA9IGcubm9kZXMuY250W2IuZ3JhcGgubmFtZXMuc3ViLmJpZ10sIAogICAgICAgICAgICAgIGNvbG9yID0gZy5ub2Rlcy50eXBlW2IuZ3JhcGgubmFtZXMuc3ViLmJpZ10sCiAgICAgICAgICAgICAgbW9kZSA9ICdrYW1hZGFrYXdhaScsCiAgICAgICAgICAgICAgYXJyb3cuZ2FwID0gMC4wNCwgYXJyb3cuc2l6ZSA9IDUsCiAgICAgICAgICAgICAgcGFsZXR0ZSA9IHRlLmNvbHMpICsgZ3VpZGVzKHNpemUgPSBGKSsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQogIAogIAogIHNldC5zZWVkKDIwKQogIHAuZmFtIDwtIGdnbmV0MihnLnBhcnQuc3ViLmJpZywgbGFiZWwgPSBnLm5vZGVzLmNudFtiLmdyYXBoLm5hbWVzLnN1Yi5iaWddLCBlZGdlLmNvbG9yID0gImJsYWNrIiwgCiAgICAgICAgICAgICAgbm9kZS5zaXplID0gZy5ub2Rlcy5jbnRbYi5ncmFwaC5uYW1lcy5zdWIuYmlnXSwKICAgICAgICAgICAgICAjIG5vZGUuc2l6ZSA9IDEwLCAKICAgICAgICAgICAgICBjb2xvciA9IGcubm9kZXMuZmFtW2IuZ3JhcGgubmFtZXMuc3ViLmJpZ10sCiAgICAgICAgICAgICAgbW9kZSA9ICdrYW1hZGFrYXdhaScsCiAgICAgICAgICAgICAgYXJyb3cuZ2FwID0gMC4wNCwgYXJyb3cuc2l6ZSA9IDUsCiAgICAgICAgICAgICAgcGFsZXR0ZSA9IGZhbS5wYWxldHRlKSArIGd1aWRlcyhzaXplID0gRikrIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKICAKICBwcCA9IGdnYXJyYW5nZShwLnRlLCBwLmZhbSwgbnJvdz0xKQogIAogICMgcHAKICAKICAjIFRPRE86IHRoaXJkIHBsb3QgLSBmcmVxdWVuY3kgcGxvdCAodGhyZWUgb2YgY29sb3JzOiBpbnNlcnRpb24sIGRlbGV0aW9uLCBpbmRlbCkKICAKICBwZGYocGFzdGUocGF0aC5jb21wb25lbnRzLCAnZ3JhcGhfbW9iX2NvbXBvbmVudF8nLCBzcHJpbnRmKCIlMDNkIiwgaS50YXJnZXQpLCcucGRmJywgc2VwID0gJycpLCAKICAgICAgd2lkdGggPSA2LCBoZWlnaHQgPSAzKQogIHByaW50KHBwKSAgICAgIyBQbG90IDEgLS0+IGluIHRoZSBmaXJzdCBwYWdlIG9mIFBERgogIGRldi5vZmYoKQoKfQoKCgpgYGAKIyMjIEdldCBzZXF1ZW5jZXMgYW5kIGFsaWduCmBgYHtyfQoKcGF0aC5tYWZmdCA9IHBhc3RlKHBhdGgud29yaywgJ21hZmZ0LycsIHNlcCA9ICcnKQppZiAoIWRpci5leGlzdHMocGF0aC5tYWZmdCkpIHsKICBkaXIuY3JlYXRlKHBhdGgubWFmZnQpCn0KICAKc3YubmFtZS50YXJnZXQgPSBub2RlcyR0ZVtub2RlcyRub2RlICVpbiUgdG1wLm5hbWVzXQoKc2Vxcy50YXJnZXQgPSBzdi5zZXFzW3N2Lm5hbWUudGFyZ2V0XQpzZXFzLnRhcmdldC5sZW4gPSBhcy5udW1lcmljKHNhcHBseShuYW1lcyhzZXFzLnRhcmdldCksIGZ1bmN0aW9uKHMpIHN0cnNwbGl0KHMsICdcXHwnKVtbMV1dWzJdKSkKc2Vxcy50YXJnZXQgPSBzZXFzLnRhcmdldFtvcmRlcigtc2Vxcy50YXJnZXQubGVuKV0Kc3YubmFtZS50YXJnZXQgPSBuYW1lcyhzZXFzLnRhcmdldCkKCiMgRmluZCBvcmllbnRhdGlvbnMgb2Ygc2VxdWVuY2VzCgoKYmwudGFyZ2V0ID0gYmwucmVzWyhibC5yZXMkVjEgJWluJSBzdi5uYW1lLnRhcmdldCkgJiAoYmwucmVzJFY4ICVpbiUgc3YubmFtZS50YXJnZXQpLF0Kb3JpZW50YXRpb24udGFyZ2V0ID0gcmVwKCcuJywgbGVuZ3RoKHNlcXMudGFyZ2V0KSkKbmFtZXMob3JpZW50YXRpb24udGFyZ2V0KSA9IHN2Lm5hbWUudGFyZ2V0Cm9yaWVudGF0aW9uLnRhcmdldFsxXSA9ICcrJwoKIyBUT0RPOiBkZWZpbmUgdGhlIGZpcnN0IG9yaWVudGF0aW9uIGJ5IHRoZSBsb25nZXN0IE9SRgoKZGlyLnNlcSA9IGMoJy0nLCAnKycpCgpmb3IoaSBpbiAxOihsZW5ndGgob3JpZW50YXRpb24udGFyZ2V0KSAtIDEpKXsKICBibC50bXAgPSBibC50YXJnZXRbYmwudGFyZ2V0JFYxID09IHN2Lm5hbWUudGFyZ2V0W2ldLF0KICBibC50bXAgPSBibC50bXBbb3JkZXIoLWJsLnRtcCRWNyksXQogIGJsLnRtcCA9IGJsLnRtcFshZHVwbGljYXRlZChibC50bXAkVjgpLF0KICBibC50bXAkZGlyID0gYmwudG1wJFY1ID4gYmwudG1wJFY0CiAgYmwudG1wID0gYmwudG1wW29yaWVudGF0aW9uLnRhcmdldFtibC50bXAkVjhdID09ICcuJyxdCiAgaWYobnJvdyhibC50bXApID09IDApIG5leHQKICAKICBpZihvcmllbnRhdGlvbi50YXJnZXRbaV0gPT0gJysnKXsKICAgIG9yaWVudGF0aW9uLnRhcmdldFtibC50bXAkVjhdID0gZGlyLnNlcVsxICsgKGJsLnRtcCRkaXIpICogMV0KICB9CiAgCiAgaWYob3JpZW50YXRpb24udGFyZ2V0W2ldID09ICctJyl7CiAgICBvcmllbnRhdGlvbi50YXJnZXRbYmwudG1wJFY4XSA9IGRpci5zZXFbMiAtIChibC50bXAkZGlyKSAqIDFdCiAgfQogIAogIGlmKHN1bShvcmllbnRhdGlvbi50YXJnZXQgPT0gJy4nKSA9PSAwKSBicmVhawp9CiMgCmZvcihpIGluIHdoaWNoKG9yaWVudGF0aW9uLnRhcmdldCA9PSAnLScpKXsKICBzZXFzLnRhcmdldFtbaV1dID0gcmV2KHNlcWlucjo6Y29tcChzZXFzLnRhcmdldFtbaV1dKSkKfQoKc2Vxcy50YXJnZXQubXNhID0gdW5saXN0KGxhcHBseShzZXFzLnRhcmdldCwgZnVuY3Rpb24ocykgcGFzdGUwKHMsIGNvbGxhcHNlID0gJycpKSkKc2Vxcy50YXJnZXQubXNhIDwtIEROQVN0cmluZ1NldChzZXFzLnRhcmdldC5tc2EpCgojIGFsaWdubWVudCA8LSBtc2Eoc2Vxcy50YXJnZXQubXNhKQoKCgojIFJ1biB0aGUgYWxpZ25tZW50CnRtcC5mYXN0YSA9IHBhc3RlKHBhdGgubWFmZnQsICd0bXAuZmFzdGEnLCBzZXAgPSAnJykKYWxuLmZhc3RhID0gcGFzdGUocGF0aC5tYWZmdCwgJ2Fsbi5mYXN0YScsIHNlcCA9ICcnKQp3cml0ZVhTdHJpbmdTZXQoc2Vxcy50YXJnZXQubXNhLCBmaWxlcGF0aCA9IHRtcC5mYXN0YSkKCnN5c3RlbShwYXN0ZSgnbWFmZnQgLS1vcCA1IC0tcXVpZXQgLS1tYXhpdGVyYXRlIDEwMCAnLCB0bXAuZmFzdGEsICc+JywgYWxuLmZhc3RhLCAgc2VwID0gJyAnKSkKCmFsaWdubWVudCA9IHJlYWRETkFTdHJpbmdTZXQoYWxuLmZhc3RhKQoKc2Vxcy5teCA9IGFzLm1hdHJpeChhbGlnbm1lbnQpCm1zYXBsb3Qoc2Vxcy5teCkKCmBgYAoKIyMjIEFsaWdubWVudCBzdGVwLWJ5IHN0ZXAKYGBge3J9CiMgQWxpZ24gYWxsIHNlcXVlbmNlcyB3aXRoaW4gZWFjaCBub2RlCgojIHN2Lm5hbWUudGFyZ2V0ID0gbm9kZXMkdGVbbm9kZXMkbm9kZSAlaW4lIHRtcC5uYW1lc10KCmFsbi5ub2RlcyA9IGxpc3QoKQpzZXFzLmFsbC5jb25zID0gYygpCmZvcihzLm5vZGUgaW4gdG1wLm5hbWVzKXsKICBzdi5uYW1lLm5vZGUgPSBub2RlcyR0ZVtub2RlcyRub2RlICVpbiUgcy5ub2RlXQogIHNlcXMubmFtZS5ub2RlID0gc2Vxcy50YXJnZXQubXNhW3N2Lm5hbWUubm9kZV0KICAKICBpZihsZW5ndGgoc3YubmFtZS5ub2RlKSA9PSAxKXsKICAgIHNlcXMubXggPSBhcy5tYXRyaXgoc2Vxcy5uYW1lLm5vZGUpCiAgICBhbG4ubm9kZXNbW3Mubm9kZV1dID0gc2Vxcy5teAogICAgc2Vxcy5hbGwuY29uc1tzLm5vZGVdID0gYXMuY2hhcmFjdGVyKHNlcXMubmFtZS5ub2RlKQogICAgbmV4dAogIH0KICAKICB0bXAuZmFzdGEgPSBwYXN0ZShwYXRoLm1hZmZ0LCAndG1wLmZhc3RhJywgc2VwID0gJycpCiAgYWxuLmZhc3RhID0gcGFzdGUocGF0aC5tYWZmdCwgJ2Fsbi5mYXN0YScsIHNlcCA9ICcnKQogIHdyaXRlWFN0cmluZ1NldChzZXFzLm5hbWUubm9kZSwgZmlsZXBhdGggPSB0bXAuZmFzdGEpCiAgCiAgc3lzdGVtKHBhc3RlKCdtYWZmdCAtLW9wIDUgLS1xdWlldCAtLW1heGl0ZXJhdGUgMTAwICcsIHRtcC5mYXN0YSwgJz4nLCBhbG4uZmFzdGEsICBzZXAgPSAnICcpKQogIAogIGFsaWdubWVudCA9IHJlYWRETkFTdHJpbmdTZXQoYWxuLmZhc3RhKVtzdi5uYW1lLm5vZGVdCiAgc2Vxcy5teCA9IGFzLm1hdHJpeChhbGlnbm1lbnQpCiAgIyBtc2FwbG90KHNlcXMubXgpCiAgCiAgIyBhZGQgY29uc2Vuc3VzIHNlcXVlbmNlCgogIGNvbnMucHJvZiA9IHNlcWlucjo6Y29uc2Vuc3VzKHNlcXMubXgsIG1ldGhvZCA9ICdwcm9maWxlJykKICBjb25zLnByb2YgPSBjb25zLnByb2ZbIShyb3duYW1lcyhjb25zLnByb2YpID09ICctJyksXQogIG1heC5pbmRleGVzIDwtIGFwcGx5KGNvbnMucHJvZiwgMiwgd2hpY2gubWF4KQogIGNvbnMuc2VxID0gcm93bmFtZXMoY29ucy5wcm9mKVttYXguaW5kZXhlc10KICAjIHNlcXMubXggPSByYmluZChzZXFzLm14LCBjb25zLnNlcSkKICAKICBhbG4ubm9kZXNbW3Mubm9kZV1dID0gc2Vxcy5teAogIHNlcXMuYWxsLmNvbnNbcy5ub2RlXSA9IHBhc3RlMChjb25zLnNlcSwgY29sbGFwc2UgPSAnJykKfQoKIyBDb21iaW5lIGFsbCBzZXFzIGZvciB0aGUgYWxpZ25tZW50CiAKIyBBbGlnbiBjb25zZW5zdXNlcwpzZXFzLmFsbC5jb25zIDwtIEROQVN0cmluZ1NldChzZXFzLmFsbC5jb25zKQoKIyBSdW4gdGhlIGFsaWdubWVudAp0bXAuZmFzdGEgPSBwYXN0ZShwYXRoLm1hZmZ0LCAndG1wLmZhc3RhJywgc2VwID0gJycpCmFsbi5mYXN0YSA9IHBhc3RlKHBhdGgubWFmZnQsICdhbG4uZmFzdGEnLCBzZXAgPSAnJykKd3JpdGVYU3RyaW5nU2V0KHNlcXMuYWxsLmNvbnMsIGZpbGVwYXRoID0gdG1wLmZhc3RhKQoKc3lzdGVtKHBhc3RlKCdtYWZmdCAtLW9wIDUgLS1xdWlldCAtLW1heGl0ZXJhdGUgMTAwICcsIHRtcC5mYXN0YSwgJz4nLCBhbG4uZmFzdGEsICBzZXAgPSAnICcpKQoKYWxpZ25tZW50ID0gcmVhZEROQVN0cmluZ1NldChhbG4uZmFzdGEpCgpzZXFzLm14LmNvbnMgPSBhcy5tYXRyaXgoYWxpZ25tZW50KQptc2FwbG90KHNlcXMubXguY29ucykKCiMgQ29tYmluZSBjb25zZW5zdXNlcyBiYWNrCnNlcXMubXguY29tcGxldGUgPSBjKCkKZm9yKHMubm9kZSBpbiByb3duYW1lcyhzZXFzLm14LmNvbnMpKXsKICBhbG4udG1wID0gYWxuLm5vZGVzW1tzLm5vZGVdXQogIG14LnRtcCA9IG1hdHJpeCgnLScsIG5yb3cgPSBucm93KGFsbi50bXApLCBuY29sID0gbmNvbChzZXFzLm14LmNvbnMpLCBkaW1uYW1lcyA9IGxpc3Qocm93bmFtZXMoYWxuLnRtcCksIE5VTEwpKQogIG14LnRtcFssIHNlcXMubXguY29uc1tzLm5vZGUsXSAhPSAnLSddID0gYWxuLnRtcAogIHNlcXMubXguY29tcGxldGUgPSByYmluZChzZXFzLm14LmNvbXBsZXRlLCBteC50bXApCn0KCnAubXNhID0gbXNhcGxvdChzZXFzLm14LmNvbXBsZXRlW3N2Lm5hbWUudGFyZ2V0LF0pCiMgcC5tc2EgPSBtc2FwbG90KHNlcXMubXguY29tcGxldGUpCnAubXNhCgoKcGRmKHBhc3RlKHBhdGguY29tcG9uZW50cywgJ2dyYXBoX21vYl9jb21wb25lbnRfJywgc3ByaW50ZigiJTAzZCIsIGkudGFyZ2V0KSwnX21zYS5wZGYnLCBzZXAgPSAnJyksIAogICAgd2lkdGggPSA2LCBoZWlnaHQgPSA0KQpwcmludChwLm1zYSkgICAgICMgUGxvdCAxIC0tPiBpbiB0aGUgZmlyc3QgcGFnZSBvZiBQREYKZGV2Lm9mZigpCgpgYGAKIyMjIENyZWF0ZSBncmFwaCBmcm9tIHdpdGhvdXQgbm9kZSBjb2xsYXBzZQpgYGB7cn0KCgoKYGBgCgoKCgpgYGB7cn0KcyA9IHJvd25hbWVzKHNlcXMubXgpCgpzMSA9IGFzLmNoYXJhY3Rlcihhcy52ZWN0b3Ioc2Vxcy50YXJnZXQubXNhW1sxXV0pKQpzMiA9IGFzLmNoYXJhY3Rlcihhcy52ZWN0b3Ioc2Vxcy50YXJnZXQubXNhW1s0XV0pKQpkb3RwbG90KHMxLCBzMiwgMTAsIDkpCgpgYGAKCgpgYGB7cn0KCnMxID0gYXMuY2hhcmFjdGVyKGFzLnZlY3RvcihzZXFzLnRhcmdldC5tc2FbWydTVmdyXzFfaWRfMTIyOTM2fDYzOCddXSkpCnMyID0gYXMuY2hhcmFjdGVyKGFzLnZlY3RvcihzZXFzLnRhcmdldC5tc2FbWyJTVmdyXzVfaWRfMTM2MDgzfDY3NCJdXSkpCmRvdHBsb3QoczEsIHMyLCAxMCwgOCkKCgpibC50YXJnZXRbKGJsLnRhcmdldCRWMSAlaW4lIHMudG1wKSAmIChibC50YXJnZXQkVjggJWluJSBzLnRtcCksIF0KCmBgYAoKCmBgYHtyfQoKczEgPSBhcy5jaGFyYWN0ZXIoYXMudmVjdG9yKHNlcXMudGFyZ2V0Lm1zYVtbMV1dKSkKczIgPSBhcy5jaGFyYWN0ZXIoYXMudmVjdG9yKHNlcXMudGFyZ2V0Lm1zYVtbNF1dKSkKZG90cGxvdChzMSwgczIsIDEwLCA4KQoKCnMyID0gYXMuY2hhcmFjdGVyKHNlcXMudGFyZ2V0Lm1zYVtbMV1dKQojIHRvZG86ICVPUkYKYGBgCgoKIyBTdG9wCmBgYHtyfQpzdG9wKCdBbGwgZ3JhcGhzIGFyZSBjcmVhdGVkLCBTdG9wIGJlZm9yZSB0aGUgY29kZSBmb3IgXCJwZXIgYWNjZXNzaW9uXCIgJykKYGBgCgoKCiMjIyBSdW4gYnkgYWNjZXNzaW9ucwpgYGB7cn0KcGF0aC5maWd1cmVzLmFjYyA9ICcvVm9sdW1lcy9TYW1zdW5nX1Q1L3ZpZW5uL3dvcmtfdGUvZmlndXJlc190ZWdyYXBoX2FjY2Vzc2lvbnMvJwpzdi5iaW4gPSByZWFkLnRhYmxlKCcvVm9sdW1lcy9TYW1zdW5nX1Q1L3ZpZW5uL3dvcmtfc3Yvc3ZzX3NlX2Jpbl92MDMudHh0Jywgc3RyaW5nc0FzRmFjdG9ycyA9IEYsIGNoZWNrLm5hbWVzID0gRkFMU0UpCmBgYAoKCmBgYHtyfQojIGFjYyA9ICcxMDAwMicKCmZvcihhY2MgaW4gY29sbmFtZXMoc3YuYmluKSl7CiAgc3YuYWNjID0gcm93bmFtZXMoc3YuYmluKVtzdi5iaW5bLGFjY10gPT0gMV0KICByb3duYW1lcyhzdi5zZSkgPSBzdi5zZSRncgogIHN2LmFjYyA9IHN2LnNlW3N2LmFjYywgJ25hbWUnXQogIAogIHN2LmFjYyA9IGludGVyc2VjdChzdi5hY2MsIHJvd25hbWVzKG5vZGVzKSkKICBub2Rlcy5jbnQuYWNjID0gdGFibGUobm9kZXNbc3YuYWNjLCdub2RlJ10pCiAgCiAgCiAgc3YuYWxwaGEgPSByZXAoMCwgbGVuZ3RoKGIuZ3JhcGgubmFtZXMpKQogIG5hbWVzKHN2LmFscGhhKSA9IGIuZ3JhcGgubmFtZXMKICBzdi5hbHBoYVtuYW1lcyhzdi5hbHBoYSkgJWluJSBuYW1lcyhub2Rlcy5jbnQuYWNjKV0gPSAxCiAgCiAgIyBzZXQuc2VlZCgyMzkpCiAgIyBwIDwtIGdnbmV0MihnLnBhcnQsIGxhYmVsID0gRiwgZWRnZS5jb2xvciA9ICJibGFjayIsIAogICMgICAgICAgICAgICAgbm9kZS5zaXplID0gZy5ub2Rlcy5jbnRbYi5ncmFwaC5uYW1lc10sIAogICMgICAgICAgICAgICAgY29sb3IgPSBnLm5vZGVzLmZhbVtiLmdyYXBoLm5hbWVzXSwKICAjICAgICAgICAgICAgIGFscGhhID0gc3YuYWxwaGEsCiAgIyAgICAgICAgICAgICAjIG1vZGUgPSAna2FtYWRha2F3YWknLAogICMgICAgICAgICAgICAgIyBhcnJvdy5nYXAgPSAwLCAKICAjICAgICAgICAgICAgICMgYXJyb3cuc2l6ZSA9IDMsCiAgIyAgICAgICAgICAgICBwYWxldHRlID0gZmFtLnBhbGV0dGUpICsgZ3VpZGVzKHNpemUgPSBGKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKICAKICBzZXQuc2VlZCgyMCkKICBwIDwtIGdnbmV0MihnLnBhcnQuc3ViLnNtYWxsLCBsYWJlbCA9IEYsIGVkZ2UuY29sb3IgPSAiYmxhY2siLCAKICAgICAgICAgICAgbm9kZS5zaXplID0gZy5ub2Rlcy5jbnRbYi5ncmFwaC5uYW1lcy5zdWIuc21hbGxdLCAKICAgICAgICAgICAgY29sb3IgPSBnLm5vZGVzLmZhbVtiLmdyYXBoLm5hbWVzLnN1Yi5zbWFsbF0sCiAgICAgICAgICAgIGFscGhhID0gc3YuYWxwaGFbYi5ncmFwaC5uYW1lcy5zdWIuc21hbGxdLAogICAgICAgICAgICAjIG1vZGUgPSAna2FtYWRha2F3YWknLAogICAgICAgICAgICBwYWxldHRlID0gZmFtLnBhbGV0dGUpICsgZ3VpZGVzKHNpemUgPSBGKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCiAgcGRmKHBhc3RlKHBhdGguZmlndXJlcy5hY2MsICdncmFwaF90ZScsIHByZWZpeC5tb2RlW3NpbmdsZXRvbi5tb2RlKzFdICwnX3NtYWxsX2FjY18nLGFjYywnLnBkZicsIHNlcCA9ICcnKSwgd2lkdGggPSA1LCBoZWlnaHQgPSA1KQogIHByaW50KHApICAgICAjIFBsb3QgMSAtLT4gaW4gdGhlIGZpcnN0IHBhZ2Ugb2YgUERGCiAgZGV2Lm9mZigpCiAgCiAgCiAgc2V0LnNlZWQoMjApCiAgcCA8LSBnZ25ldDIoZy5wYXJ0LnN1Yi5iaWcsIGxhYmVsID0gRiwgZWRnZS5jb2xvciA9ICJibGFjayIsIAogICAgICAgICAgICBub2RlLnNpemUgPSBnLm5vZGVzLmNudFtiLmdyYXBoLm5hbWVzLnN1Yi5iaWddLCAKICAgICAgICAgICAgY29sb3IgPSBnLm5vZGVzLmZhbVtiLmdyYXBoLm5hbWVzLnN1Yi5iaWddLAogICAgICAgICAgICBhbHBoYSA9IHN2LmFscGhhW2IuZ3JhcGgubmFtZXMuc3ViLmJpZ10sCiAgICAgICAgICAgIG1vZGUgPSAna2FtYWRha2F3YWknLAogICAgICAgICAgICBwYWxldHRlID0gZmFtLnBhbGV0dGUpICsgZ3VpZGVzKHNpemUgPSBGKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCiAgcGRmKHBhc3RlKHBhdGguZmlndXJlcy5hY2MsICdncmFwaF90ZScsIHByZWZpeC5tb2RlW3NpbmdsZXRvbi5tb2RlKzFdICwnX2JpZ19hY2NfJyxhY2MsJy5wZGYnLCBzZXAgPSAnJyksIHdpZHRoID0gNSwgaGVpZ2h0ID0gNSkKICBwcmludChwKSAgICAgIyBQbG90IDEgLS0+IGluIHRoZSBmaXJzdCBwYWdlIG9mIFBERgogIGRldi5vZmYoKQoKfQoKcCAKCgoKYGBgCgoKYGBge3J9CnN2LmFubm90ID0gcmVhZC50YWJsZSgnL1ZvbHVtZXMvU2Ftc3VuZ19UNS92aWVubi93b3JrX3N2L3N2c19hbm5vdGF0aW9uX3YwMy50eHQnLCBzdHJpbmdzQXNGYWN0b3JzID0gRikKcm93bmFtZXMoc3YuYW5ub3QpID0gc3YuYW5ub3QkZ3IKaGVhZChzdi5hbm5vdCkKCnN2LmFubm90W2V4dHJhY3RlZF92YWx1ZXMsXQoKYGBgCgoKCgojIEJpZyBURS1ub2RlcwpgYGB7cn0Kbi5hbW91bnQgPSAyMAoKZy5wYXJ0IDwtIG5ldHdvcmsoYi5ncmFwaCwgbWF0cml4LnR5cGUgPSAiZWRnZWxpc3QiLCBpZ25vcmUuZXZhbCA9IEZBTFNFLCBkaXJlY3RlZCA9IFRSVUUpCmIuZ3JhcGgubmFtZXMgPSBuZXR3b3JrLnZlcnRleC5uYW1lcyhnLnBhcnQpCgpzaXplLmJpZyA9IGcubm9kZXMuY250W2IuZ3JhcGgubmFtZXNdCmFscGhhLmJpZyA9IHJlcCgxLCBsZW5ndGgoYi5ncmFwaC5uYW1lcykpCm5hbWVzKGFscGhhLmJpZykgPSBiLmdyYXBoLm5hbWVzCmFscGhhLmJpZ1tzaXplLmJpZyA8IG4uYW1vdW50XSA9IDAKCnN1bShzaXplLmJpZyA+PSBuLmFtb3VudCkKCnNldC5zZWVkKDIwKQpwIDwtIGdnbmV0MihnLnBhcnQsIGxhYmVsID0gRiwgZWRnZS5jb2xvciA9ICJibGFjayIsIAogICAgICAgICAgICBub2RlLnNpemUgPSBzaXplLmJpZywgCiAgICAgICAgICAgIGNvbG9yID0gZy5ub2Rlcy5mYW1bYi5ncmFwaC5uYW1lc10sCiAgICAgICAgICAgIGFscGhhPSBhbHBoYS5iaWcsCiAgICAgICAgICAgICMgbW9kZSA9ICdrYW1hZGFrYXdhaScsCiAgICAgICAgICAgICMgYXJyb3cuZ2FwID0gMCwgCiAgICAgICAgICAgICMgYXJyb3cuc2l6ZSA9IDMsCiAgICAgICAgICAgIHBhbGV0dGUgPSBmYW0ucGFsZXR0ZSkgKyBndWlkZXMoc2l6ZSA9IEYpICsgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKG5jb2wgPSAyKSkKcAoKcGRmKHBhc3RlKHBhdGguZmlndXJlcywgJ2dyYXBoX3NtYWxsX2NsdXN0ZXInLCBwcmVmaXgubW9kZVtzaW5nbGV0b24ubW9kZSsxXSAsJ19mYW1pbHlfYW1vdW50LnBkZicsIHNlcCA9ICcnKSwgd2lkdGggPSA1LCBoZWlnaHQgPSA1KQpwcmludChwKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpKSAgICAgIyBQbG90IDEgLS0+IGluIHRoZSBmaXJzdCBwYWdlIG9mIFBERgpkZXYub2ZmKCkKCgoKCgpgYGAKCiMjIFdoaWNoIGZhbWlsaWVzIHNwZWNpZmljYWxseSwgYW5kIGlzIHRoZSByYXRlIG9mIGluc2VydGlvbiBpcyBkaWZmZXJlbnQ/CmNvbXBhcmUgbnVtYmVyIG9mIGluc2VydGlvbnMgd2l0aCB0aGUgdG90YWwgbnVtYmVyIG9mIFRFIGxvYWQKYGBge3J9CgpiaWcuZmFtaWxpZXMgPSBkYXRhLmZyYW1lKG5vZGUgPSAgbmFtZXMoc2l6ZS5iaWcpW3NpemUuYmlnID49IG4uYW1vdW50XSkKYmlnLmZhbWlsaWVzJHNpemUgPSBzaXplLmJpZ1tiaWcuZmFtaWxpZXMkbm9kZV0KYmlnLmZhbWlsaWVzJGZhbSA9IGcubm9kZXMuZmFtW2JpZy5mYW1pbGllcyRub2RlXQpiaWcuZmFtaWxpZXMgPSBiaWcuZmFtaWxpZXNbb3JkZXIoLWJpZy5mYW1pbGllcyRzaXplKSxdCnJvd25hbWVzKGJpZy5mYW1pbGllcykgPSBOVUxMCgpub2RlLmJpZyA9IG5vZGVzW25vZGVzJG5vZGUgJWluJSBiaWcuZmFtaWxpZXMkbm9kZSxdCgp2ID0gcmVhZC50YWJsZShwYXN0ZShwYXRoLndvcmssICdibGFzdF9zdl9vbl90ZXMudHh0Jywgc2VwID0gJycpKQp2ID0gdlt2JFYxICVpbiUgbm9kZS5iaWckdGUsXQoKCnBvcy5sZW4xID0gMgpwb3MubGVuMiA9IDUKdjEubGVuID0gc2FwcGx5KHVuaXF1ZSh2JFYxKSwgZnVuY3Rpb24ocykgYXMubnVtZXJpYyhzdHJzcGxpdChzLCdcXHwnKVtbMV1dW3Bvcy5sZW4xXSkpCnY4LmxlbiA9IHNhcHBseSh1bmlxdWUodiRWOCksIGZ1bmN0aW9uKHMpIGFzLm51bWVyaWMoc3Ryc3BsaXQocywnXFx8JylbWzFdXVtwb3MubGVuMl0pKQp2LmxlbiA9IGModjEubGVuLCB2OC5sZW4pCgp2LnNpbSA9IGZpbmROZXN0ZWRuZXNzKHYsIHVzZS5zdHJhbmQgPSBGKQoKdi5zaW0gPSBmaW5kTmVzdGVkbmVzcyh2LCB1c2Uuc3RyYW5kID0gRikKdi5zaW0kcDEgPSB2LnNpbSRDMSAvIHYubGVuW3Yuc2ltJFYxXQp2LnNpbSRwOCA9IHYuc2ltJEM4IC8gdi5sZW5bdi5zaW0kVjhdCnYuc2ltJHAxLmluOCA9IHYuc2ltJEMxIC8gdi5sZW5bdi5zaW0kVjhdCnYuc2ltJHA4LmluMSA9IHYuc2ltJEM4IC8gdi5sZW5bdi5zaW0kVjFdCgpub2RlLmJpZyRzdWJmYW0gPSAnJwpmb3Ioc3YubmFtZSBpbiB1bmlxdWUodi5zaW0kVjEpKXsKICB2LnRtcCA9IHYuc2ltW3Yuc2ltJFYxID09IHN2Lm5hbWUsXQogIHMgPSB2LnRtcCRWOFt3aGljaC5tYXgodi50bXAkcDEpXQogIHMgPSBzdHJzcGxpdChzLCAnXFx8JylbWzFdXVs4XQogIG5vZGUuYmlnW3N2Lm5hbWUsICdzdWJmYW0nXSA9IHMKfQoKCnggPSB0YXBwbHkobm9kZS5iaWckc3ViZmFtLCBub2RlLmJpZyRub2RlLCBmdW5jdGlvbih4KXsKICBjbnQgPSB0YWJsZSh4KQogIHggPSBuYW1lcyhjbnQpW2NudCA9PSBtYXgoY250KV0KICByZXR1cm4ocGFzdGUwKHgsIGNvbGxhcHNlID0gICcsJykpCn0pCgpiaWcuZmFtaWxpZXMkc3ViZmFtID0geFtiaWcuZmFtaWxpZXMkbm9kZV0KCgpgYGAKCgoKIyBuby1URSBTVgojIyBDb25zdHJ1Y3QKYGBge3J9CnN2LnNlID0gcmVhZFJEUyhwYXN0ZShwYXRoLnN2cywgJ3N2X3NlLnJkcycsIHNlcCA9ICcnKSkKc2ltLmN1dG9mZiA9IDAuODUKCgpzdi5zZS5uby50ZSA9IHN2LnNlJG5hbWVbKHN2LnNlJHRlID09ICdub1RFJykgJiAoc3Yuc2UkbGVuID4gNTApXQoKYmwuZmlsZSA9IHBhc3RlKHBhdGgud29yaywnc3ZfYmlnX29uX2JpZy50eHQnLCBzZXAgPSAnJykKYmwuc3YgPSByZWFkLnRhYmxlKGJsLmZpbGUsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpibC5zdiA9IGJsLnN2W2JsLnN2JFYxICE9IGJsLnN2JFY4LF0KCiMgcmVtb3ZlIGhhdmluZyBURXMKYmwuc3YgPSBibC5zdltibC5zdiRWMSAlaW4lIHN2LnNlLm5vLnRlLCBdCmJsLnN2ID0gYmwuc3ZbYmwuc3YkVjggJWluJSBzdi5zZS5uby50ZSwgXQoKcG9zLmxlbjEgPSAyCnN2LmxlbiA9IHNhcHBseSh1bmlxdWUoYyhibC5zdiRWMSwgYmwuc3YkVjgpKSwgZnVuY3Rpb24ocykgYXMubnVtZXJpYyhzdHJzcGxpdChzLCdcXHwnKVtbMV1dW3Bvcy5sZW4xXSkpCmJsLnN2JGxlbjEgPSBzdi5sZW5bYmwuc3YkVjFdCmJsLnN2JGxlbjggPSBzdi5sZW5bYmwuc3YkVjhdCm1heC5sZW4gPSAyMDAwMApibC5zdiA9IGJsLnN2WyhibC5zdiRsZW4xIDw9IG1heC5sZW4pICYgKGJsLnN2JGxlbjggPD0gbWF4LmxlbiksXQpibC5zdiRwMSA9IChibC5zdiRWMyAtIGJsLnN2JFYyICsgMSkgLyBibC5zdiRsZW4xCmJsLnN2JHA4ID0gKGFicyhibC5zdiRWNSAtIGJsLnN2JFY0KSArIDEpIC8gYmwuc3YkbGVuOApibC5zdiRjb21iID0gYXMuZmFjdG9yKHBhc3RlKGJsLnN2JFYxLCBibC5zdiRWOCwgc2VwID0gJ3x8JykpCgppZHgubXV0dWFsID0gKGJsLnN2JHAxID49IHNpbS5jdXRvZmYpICYgKGJsLnN2JHA4ID49IHNpbS5jdXRvZmYpCiMgVGhlcmUgaXMgYSBiaWcgZGlzY3Vzc2lvbiBpbiBteSBoZWFkLCB3aGV0aGVyIGl0IHNob3VsZCBiZSAnJicgb3IgJ3wnCiMgSWYgaXQncyBub3QgLHV0dWFsLCB0aGVuIG1heWJlIHdpdGggc29tZXRoaW5nIGVsc2UgaXQgd2lsbCBjb25zdHJ1Y3QgYSBtdXR1YWwgcmVsYXRpb24sIAojIHNvIHdlIHNob3VsZCByZW1haW4gZm9yIHRoZSBhbmFseXNpcyBvZiBuZXN0ZWRuZXNzIGFsbCBwYXJ0aWFsIGluY2x1c2lvbnMKc3YubXV0dWFsID0gYmwuc3ZbaWR4Lm11dHVhbCwgXQp2ID0gYmwuc3ZbIWlkeC5tdXR1YWwsIF0KdiA9IHZbISh2JGNvbWIgJWluJSBzdi5tdXR1YWwkY29tYiksXQoKIyBBdCBzb21lIHBvaW50IGl0IHdhcyBhIHN0ZXAgdG8gcmVtYWluIG9ubHkgdGhvc2UgaW5zdGFuY2VzIHdoaWNoIGFyZSBub3QgInVuaXF1ZSIgaW4gY29tYmluYXRpb25zCiMgYnV0IEkgdGhpbmsgaXQncyBub3QgY29ycmVjdCBoZXJlCgpzdi5zaW0gPSBmaW5kTmVzdGVkbmVzcyh2LCB1c2Uuc3RyYW5kID0gVCkKc3Yuc2ltJHAxID0gc3Yuc2ltJEMxIC8gc3YubGVuW3N2LnNpbSRWMV0Kc3Yuc2ltJHA4ID0gc3Yuc2ltJEM4IC8gc3YubGVuW3N2LnNpbSRWOF0KCiMgaGVyZSAgd2Ugc2hvdWxkIGZpbmFsbHkgdXNlICd8Jywgbm90ICcmJwpzdi5uZXN0ZWQgPSBzdi5zaW1bKHN2LnNpbSRwMSA+PSBzaW0uY3V0b2ZmKSB8IChzdi5zaW0kcDggPj0gc2ltLmN1dG9mZikgLF0KCiMgQ3JlYXRlIHByZS1kYXRhIGZvciBkZWZpbmluZyBlZGdlcwpjb21tb24ubmFtZXMgPSBpbnRlcnNlY3QoY29sbmFtZXMoc3YubXV0dWFsKSwgY29sbmFtZXMoc3YubmVzdGVkKSkKc3Yub3ZlcmFsbCA9IHJiaW5kKHN2Lm11dHVhbFssY29tbW9uLm5hbWVzXSwgc3YubmVzdGVkWyxjb21tb24ubmFtZXNdKQpzdi5vdmVyYWxsJGdyb3VwID0gKHN2Lm92ZXJhbGwkcDEgPj0gc2ltLmN1dG9mZikgKiAxICsgKHN2Lm92ZXJhbGwkcDggPj0gc2ltLmN1dG9mZikgKiAyCmlkeDEgPSBzdi5vdmVyYWxsJGdyb3VwICE9IDIgICMgVjEgaW4gVjgKaWR4MiA9IHN2Lm92ZXJhbGwkZ3JvdXAgIT0gMSAgIyBWOCBpbiBWMQoKCiMgRWRnZXMgCnN2LmVkZ2VzID0gcmJpbmQoY2JpbmQoc3Yub3ZlcmFsbCRWMVtpZHgxXSwgc3Yub3ZlcmFsbCRWOFtpZHgxXSksCiAgICAgICAgICAgICAgICAgY2JpbmQoc3Yub3ZlcmFsbCRWOFtpZHgyXSwgc3Yub3ZlcmFsbCRWMVtpZHgyXSkpCgoKc3YuZ3JhcGggPC0gaWdyYXBoOjptYWtlX2dyYXBoKHQoc3YuZWRnZXMpLCBkaXJlY3RlZCA9IFQpCnN2LmdyYXBoIDwtIGlncmFwaDo6c2ltcGxpZnkoc3YuZ3JhcGgpCnN2LmdyYXBoY29tcCA8LSBpZ3JhcGg6OmNvbXBvbmVudHMoc3YuZ3JhcGgpCgpzdi5tZW1iID0gZGF0YS5mcmFtZShtZW1iID0gc3YuZ3JhcGhjb21wJG1lbWJlcnNoaXApCnN2Lm1lbWIkbmFtZSA9IHJvd25hbWVzKHN2Lm1lbWIpCnJvd25hbWVzKHN2Lm1lbWIpID0gTlVMTApyb3duYW1lcyhzdi5zZSkgPSBzdi5zZSRuYW1lCnN2Lm1lbWIkdGUgPSBzdi5zZVtzdi5tZW1iJG5hbWUsICd0ZSddCnN2Lm1lbWIkY292ZXIgPSBzdi5zZVtzdi5tZW1iJG5hbWUsICdjb3ZlciddIC8gc3Yuc2Vbc3YubWVtYiRuYW1lLCAnbGVuJ10Kc3YubWVtYiRsZW4gPSBzdi5sZW5bc3YubWVtYiRuYW1lXQpgYGAKCiMjIFBsb3QgYWxsCmBgYHtyfQpnLnBhcnQgPC0gbmV0d29yayhzdi5lZGdlcywgbWF0cml4LnR5cGUgPSAiZWRnZWxpc3QiLCBpZ25vcmUuZXZhbCA9IEZBTFNFLCBkaXJlY3RlZCA9IFRSVUUpCmIuZ3JhcGgubmFtZXMgPSBuZXR3b3JrLnZlcnRleC5uYW1lcyhnLnBhcnQpCgpzZXQuc2VlZCgyMzkpCnAgPC0gZ2duZXQyKGcucGFydCwgbGFiZWwgPSBGLCBlZGdlLmNvbG9yID0gImJsYWNrIiwgCiAgICAgICAgICAgIG5vZGUuc2l6ZSA9IDEsCiAgICAgICAgICAgICMgbm9kZS5zaXplID0gZy5ub2Rlcy5jbnRbYi5ncmFwaC5uYW1lc10sIAogICAgICAgICAgICAjIGNvbG9yID0gZy5ub2Rlcy50eXBlW2IuZ3JhcGgubmFtZXNdLAogICAgICAgICAgICAjIHBhbGV0dGUgPSB0ZS5jb2xzCiAgICAgICAgICAgICkgKyBndWlkZXMoc2l6ZSA9IEYpCnAgCgpgYGAKCiMjIFBsb3Qgd2l0aCBjb2xvcnMKYGBge3J9CnN2LnByb3QuaW5pdCA9IHJlYWRSRFMocGFzdGUocGF0aC53b3JrLCAnc3ZfcHJvdGVpbnNfbm9fdGVfYmxhc3QucmRzJywgc2VwID0gJycpKQpzdi5wcm90LmluaXQkbmFtZSA9IHNhcHBseShzdi5wcm90LmluaXQkWDEsIGZ1bmN0aW9uKHMpewogIHMgPSBwYXN0ZTAoc3Ryc3BsaXQocywgJ1xcfCcpW1sxXV1bMToyXSwgY29sbGFwc2UgPSAnfCcpCiAgcmV0dXJuKHMpCn0pCnN2LnByb3QgPSBzdi5wcm90LmluaXRbc3YucHJvdC5pbml0JHByb3QgPT0gMSxdCnN2LnByb3RbLDJdID0gdG9sb3dlcihzdi5wcm90WywyXSkKCnR5cGVzID0gYygnZGlzZWFzZScsICdyZXBlYXQnLCAncmVjZXB0b3InLCAgJ3ppbmMnLCAndHJhbnNjcmlwdGFzZScsICdyZXZlcnNlJywgJ3RyYW5zcG9zJykKZm9yKGkudHlwZSBpbiAxOmxlbmd0aCh0eXBlcykpewogIHN2LnByb3RbLHR5cGVzW2kudHlwZV1dID0gKGdyZXBsKHR5cGVzW2kudHlwZV0sIHN2LnByb3RbLDJdKSkgKiAxCn0Kc3YucHJvdCR0eXBlID0gcm93U3Vtcyhzdi5wcm90Wyx0eXBlc10pCnRhYmxlKHN2LnByb3QkdHlwZSkKCgoKc3YubWVtYiRwcm90ID0gJ25vIHByb3QnCnN2Lm1lbWIkcHJvdFtzdi5tZW1iJG5hbWUgJWluJSBzdi5wcm90LmluaXQkbmFtZV0gPSAndW5kZWZpbmVkIHByb3QnCnN2Lm1lbWIkcHJvdFtzdi5tZW1iJG5hbWUgJWluJSBzdi5wcm90JG5hbWVdID0gJ2RlZmluZWQgcHJvdCcKZm9yKHR5cGUgaW4gdHlwZXMpewogIHN2Lm1lbWIkcHJvdFtzdi5tZW1iJG5hbWUgJWluJSBzdi5wcm90JG5hbWVbc3YucHJvdFssdHlwZV0gPT0gMV1dID0gdHlwZQp9CgpnLm5vZGVzLnByb3QgPSBzdi5tZW1iJHByb3QKZy5ub2Rlcy5wcm90W2cubm9kZXMucHJvdCA9PSAnZGlzZWFzZSddID0gJ2RlZmluZWQgcHJvdCcKbmFtZXMoZy5ub2Rlcy5wcm90KSA9IHN2Lm1lbWIkbmFtZQoKCgpzZXQuc2VlZCgyMzkpCnAgPC0gZ2duZXQyKGcucGFydCwgbGFiZWwgPSBGLCBlZGdlLmNvbG9yID0gImJsYWNrIiwgCiAgICAgICAgICAgICMgbm9kZS5zaXplID0gZy5ub2Rlcy5jbnRbYi5ncmFwaC5uYW1lc10sIAogICAgICAgICAgICBub2RlLnNpemUgPSAxLAogICAgICAgICAgICBjb2xvciA9IGcubm9kZXMucHJvdFtiLmdyYXBoLm5hbWVzXSwKICAgICAgICAgICAgcGFsZXR0ZSA9IGcuY29scywKICAgICAgICAgICAgIyBtb2RlID0gImthbWFkYWthd2FpIgogICAgICAgICAgICApICsgZ3VpZGVzKHNpemUgPSBGKSArIGNvb3JkX2ZpeGVkKHJhdGlvID0gMSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBnLmNvbHMsIAogICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGMoInRyYW5zcG9zIiwicmV2ZXJzZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicmVwZWF0IiwiemluYyIsInJlY2VwdG9yIiwgImRlZmluZWQgcHJvdCIsICJ1bmRlZmluZWQgcHJvdCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibm8gcHJvdCIpLCAKICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICdQcm90ZWluIGtleS13b3JkOicpICsgdGhlbWUobGVnZW5kLmp1c3RpZmljYXRpb24gPSBjKDEsIDApKQpwID0gcCsgdGhlbWUobGVnZW5kLmtleS5oZWlnaHQgPSB1bml0KDAuNSwgImNtIikpCnAKCnBkZihwYXN0ZShwYXRoLmZpZ3VyZXMsICdncmFwaF9uZXdfYWxsLnBkZicsIHNlcCA9ICcnKSwgd2lkdGggPSA2LCBoZWlnaHQgPSA0KQpwcmludChwKSAgICAgIyBQbG90IDEgLS0+IGluIHRoZSBmaXJzdCBwYWdlIG9mIFBERgpkZXYub2ZmKCkKCiMgCiMgY250ID0gdGFibGUoZy5ub2Rlcy5wcm90KQojIGNudCA9IGMoc3VtKGNudFtjKCJ0cmFuc3BvcyIsInJldmVyc2UiLCJyZXBlYXQiLCJ6aW5jIildKSwgc3VtKGNudFtjKCJyZWNlcHRvciIsImRlZmluZWQgcHJvdCIpXSksCiMgICAgICAgICBjbnRbInVuZGVmaW5lZCBwcm90Il0sIGNudFsibm8gcHJvdCJdKQoKYGBgCgojIyBUeXBlcyBvZiB0aGUgY29tcG9uZW50CgoKYGBge3J9Cgpzdi5ncmFwaCA8LSBpZ3JhcGg6Om1ha2VfZ3JhcGgodChzdi5lZGdlcyksIGRpcmVjdGVkID0gVCkKc3YuZ3JhcGggPC0gaWdyYXBoOjpzaW1wbGlmeShzdi5ncmFwaCkKc3YuZ3JhcGhjb21wIDwtIGlncmFwaDo6Y29tcG9uZW50cyhzdi5ncmFwaCkKCnN2LmNvbXAubWVtYmVyID0gc3YuZ3JhcGhjb21wJG1lbWJlcnNoaXAKCnMudGFncyA9IGMoInRyYW5zcG9zIiwicmV2ZXJzZSIsInJlcGVhdCIsInppbmMiLCAicmVjZXB0b3IiLCJkZWZpbmVkIHByb3QiLCAidW5kZWZpbmVkIHByb3QiLCAnbm8gcHJvdCcpCnMudGFnczAgPSByZXAoJycsIGxlbmd0aChzLnRhZ3MpKQpzLnRhZ3MwWzE6NF0gPSAnVEUtbGlrZScKcy50YWdzMFs1OjZdID0gJ0tub3duIFByb3RlaW5zJwpzLnRhZ3MwWzddID0gJ1VuZGVmLiBQcm90ZWlucycKcy50YWdzMFs4XSA9ICdObyBQcm90ZWlucycKbmFtZXMocy50YWdzMCkgPSBzLnRhZ3MKCmNvbXAudGFncyA9IHJlcCgnJywgbGVuZ3RoKHVuaXF1ZShzdi5jb21wLm1lbWJlcikpKQpmb3Iocy50YWcgaW4gcy50YWdzKXsKICB0bXAudGFncyA9IHVuaXF1ZShzdi5jb21wLm1lbWJlcltuYW1lcyhnLm5vZGVzLnByb3QpW2cubm9kZXMucHJvdCA9PSBzLnRhZ11dKQogIGNvbXAudGFnc1t0bXAudGFnc11bY29tcC50YWdzW3RtcC50YWdzXSA9PSAnJ10gPSBzLnRhZwp9CmNvbXAudGFnc1tjb21wLnRhZ3MgPT0gJyddID0gJ25vIHByb3QnCmNvbXAudGFncyA9IGRhdGEuZnJhbWUodGFibGUoY29tcC50YWdzKSkKY29sbmFtZXMoY29tcC50YWdzKSA9IGMoJ3RhZzEnLCAnZnJlcScpCmNvbXAudGFncyR0YWcxID0gZmFjdG9yKGNvbXAudGFncyR0YWcxLCBsZXZlbHMgPSBzLnRhZ3MpCmNvbXAudGFncyA9IGNvbXAudGFnc1tvcmRlcihjb21wLnRhZ3MkdGFnMSksXQoKY29tcC50YWdzJHRhZzAgPSBzLnRhZ3MwW2NvbXAudGFncyR0YWcxXQpjb21wLnRhZ3MkdGFnMCA9IGZhY3Rvcihjb21wLnRhZ3MkdGFnMCwgbGV2ZWxzID0gdW5pcXVlKHMudGFnczApKQoKeS50aWNrcyA9IHRhcHBseShjb21wLnRhZ3MkZnJlcSwgY29tcC50YWdzJHRhZzAsIHN1bSkKeS50aWNrcyA9IHkudGlja3NbIWlzLm5hKHkudGlja3MpXQoKeXkgPSBzdW0oeS50aWNrcykgLSBjdW1zdW0oeS50aWNrcykgKyB5LnRpY2tzLzIKCmNvbXAudGFncyR5bWluIDwtIGMoMCwgY3Vtc3VtKGNvbXAudGFncyRmcmVxKVstbGVuZ3RoKGNvbXAudGFncyRmcmVxKV0pCmNvbXAudGFncyR5bWF4IDwtIGN1bXN1bShjb21wLnRhZ3MkZnJlcSkKCnguc3RlcCA9IHJlcCgwLCA4KQpuLnN0ZXAgPSAxMAp4LnN0ZXBbYyg1LDcsOCldID0gbi5zdGVwCnguc3RlcCA9IGN1bXN1bSh4LnN0ZXApCgpjb21wLnRhZ3MkeW1pbiA9IGNvbXAudGFncyR5bWluICsgeC5zdGVwCmNvbXAudGFncyR5bWF4ID0gY29tcC50YWdzJHltYXggKyB4LnN0ZXAKCnkubWluID0gdGFwcGx5KGNvbXAudGFncyR5bWluLCBjb21wLnRhZ3MkdGFnMCwgbWluKQp5Lm1heCA9IHRhcHBseShjb21wLnRhZ3MkeW1heCwgY29tcC50YWdzJHRhZzAsIG1heCkKeS52YWwgPSAoeS5tYXggKyB5Lm1pbikgLyAyCnkuY250ID0gdGFwcGx5KGNvbXAudGFncyRmcmVxLCBjb21wLnRhZ3MkdGFnMCwgc3VtKQoKZGYudGV4dCA9IGRhdGEuZnJhbWUoeS5taW4gPSB5Lm1pbiwgeS5tYXggPSB5Lm1heCwgeS52YWwgPSB5LnZhbCwgeS5jbnQgPSB5LmNudCwgbGFiZWwgPSBuYW1lcyh5LnZhbCkpCmRmLnRleHQkYW5nbGVzIDwtIDM2MCAtIChkZi50ZXh0JHkudmFsIC8gKG1heChjb21wLnRhZ3MkeW1heCkgKyBuLnN0ZXApKSAqIDM2MCAKZGYudGV4dCRhbmdsZXNbMjozXSA9IDE4MCArIGRmLnRleHQkYW5nbGVzWzI6M10KCnAgPSBnZ3Bsb3QoY29tcC50YWdzLCBhZXMoeCA9IDAsIHkgPSBmcmVxLCBmaWxsID0gdGFnMSkpICsKICAgZ2VvbV9yZWN0KGFlcyh4bWluID0gLTAuNSwgeG1heCA9IDAuNSwgeW1pbiA9IHltaW4sIHltYXggPSB5bWF4KSkgKwogICBjb29yZF9wb2xhcigieSIsIHN0YXJ0ID0gMCkgKwogICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBnLmNvbHMucGx1cykgKyB5bGltKDAsIG1heChjb21wLnRhZ3MkeW1heCkgKyBuLnN0ZXApICsKICAgdGhlbWVfdm9pZCgpICsgeGxpbSgtMS41LCAwLjcpICsgCiAgIGdlb21fdGV4dChkYXRhPWRmLnRleHQsIGFlcyh4ID0gMC43LCB5ID0geS52YWwsIGxhYmVsID0gcGFzdGUobGFiZWwsIHkuY250LCBzZXAgPSAnOiAnKSksIAogICAgICAgICAgICAgYW5nbGUgPSBkZi50ZXh0JGFuZ2xlcywgaW5oZXJpdC5hZXMgPSBGQUxTRSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpICsgCiAgYW5ub3RhdGUoInRleHQiLCB4ID0gLTEuNSwgeSA9IDAsIGxhYmVsID0gcGFzdGUoJ1RvdGFsJyxzdW0oY29tcC50YWdzJGZyZXEpLCdcbiBjb25uZWN0ZWQgXG5jb21wb25lbnRzJykpIAoKcAoKcGRmKHBhc3RlKHBhdGguZmlndXJlcywgJ2dyYXBoX25ld19waWVfY2hhcnQucGRmJywgc2VwID0gJycpLCB3aWR0aCA9IDMuMSwgaGVpZ2h0ID0gMy4xKQpwcmludChwKSAgICAgIyBQbG90IDEgLS0+IGluIHRoZSBmaXJzdCBwYWdlIG9mIFBERgpkZXYub2ZmKCkKCmBgYAoKCgojIyMjIEkgZG9uJ3Qga25vdwpgYGB7cn0Kc3Yuc2UkZnJlcSA9IHN2LnNlJGZyZXEubWF4Cm4uY3V0b2ZmID0gMwpuID0gMjgKc3Yuc2Ukc2luID0gJ2luZGVsJwpzdi5zZSRzaW5bc3Yuc2UkZnJlcSA+PSAobiAtIG4uY3V0b2ZmKV0gPSAnZGVsZXRpb24nCnN2LnNlJHNpbltzdi5zZSRmcmVxIDw9IG4uY3V0b2ZmXSA9ICdpbnNlcnRpb24nCgoKZy5ub2Rlcy5wcm90LnNpbiA9IGcubm9kZXMucHJvdApnLm5vZGVzLnByb3Quc2luW25hbWVzKGcubm9kZXMucHJvdC5zaW4pICVpbiUgc3Yuc2UkbmFtZVtzdi5zZSRzaW4gIT0gJ2luc2VydGlvbiddIF0gPSAnbmEnCmcuY29sc1snbmEnXSA9ICd3aGl0ZScKCgoKCnNldC5zZWVkKDIzOSkKcCA8LSBnZ25ldDIoZy5wYXJ0LCBsYWJlbCA9IEYsIGVkZ2UuY29sb3IgPSAiYmxhY2siLCAKICAgICAgICAgICAgIyBub2RlLnNpemUgPSBnLm5vZGVzLmNudFtiLmdyYXBoLm5hbWVzXSwgCiAgICAgICAgICAgIG5vZGUuc2l6ZSA9IDEsCiAgICAgICAgICAgIGNvbG9yID0gZy5ub2Rlcy5wcm90LnNpbltiLmdyYXBoLm5hbWVzXSwKICAgICAgICAgICAgcGFsZXR0ZSA9IGcuY29scywKICAgICAgICAgICAgIyBtb2RlID0gImthbWFkYWthd2FpIgogICAgICAgICAgICApICsgZ3VpZGVzKHNpemUgPSBGKQpwIAoKIyAKIyBwYXRoLmZpZ3VyZXMgID0gJy9Wb2x1bWVzL1NhbXN1bmdfVDUvdmllbm4vd29ya190ZS8nCiMgcGRmKHBhc3RlKHBhdGguZmlndXJlcywgJ2dyYXBoX3N2X25vdGVfaW5zZXJ0aW9uLnBkZicsIHNlcCA9ICcnKSwgd2lkdGggPSA2LCBoZWlnaHQgPSA0KQojIHByaW50KHApICAgICAjIFBsb3QgMSAtLT4gaW4gdGhlIGZpcnN0IHBhZ2Ugb2YgUERGCiMgZGV2Lm9mZigpCgoKYWxwaGEuZWR0YSA9IHJlcCgxLCBsZW5ndGgoYi5ncmFwaC5uYW1lcykpCm5hbWVzKGFscGhhLmVkdGEpID0gYi5ncmFwaC5uYW1lcwoKc3YuYW5ub3QuYWR0YSA9IHJvd1N1bXMoc3YuYW5ub3RbLDExOm5jb2woc3YuYW5ub3QpXSA+IDAuNykgPiAwCnN2LmFubm90LmFkdGEgPSBzdi5hbm5vdC5hZHRhW3N2LnNlJGdyXQpuYW1lcyhzdi5hbm5vdC5hZHRhKSA9IHN2LnNlJG5hbWUKc3YuYW5ub3QuYWR0YSA9IHN2LmFubm90LmFkdGFbc3YuYW5ub3QuYWR0YV0KYWxwaGEuZWR0YVtuYW1lcyhhbHBoYS5lZHRhKSAlaW4lIG5hbWVzKHN2LmFubm90LmFkdGEpXSA9IDAKCgpzZXQuc2VlZCgyMzkpCnAgPC0gZ2duZXQyKGcucGFydCwgbGFiZWwgPSBGLCBlZGdlLmNvbG9yID0gImJsYWNrIiwgCiAgICAgICAgICAgICMgbm9kZS5zaXplID0gZy5ub2Rlcy5jbnRbYi5ncmFwaC5uYW1lc10sIAogICAgICAgICAgICBub2RlLnNpemUgPSAxLAogICAgICAgICAgICBhbHBoYT0xLWFscGhhLmVkdGEsCiAgICAgICAgICAgIGNvbG9yID0gZy5ub2Rlcy5wcm90W2IuZ3JhcGgubmFtZXNdLAogICAgICAgICAgICBwYWxldHRlID0gZy5jb2xzLAogICAgICAgICAgICAjIG1vZGUgPSAia2FtYWRha2F3YWkiCiAgICAgICAgICAgICkgKyBndWlkZXMoc2l6ZSA9IEYpCnAgCgoKcGRmKHBhc3RlKHBhdGguZmlndXJlcywgJ2dyYXBoX21vYl9ub3RlX2VkdGEucGRmJywgc2VwID0gJycpLCB3aWR0aCA9IDYsIGhlaWdodCA9IDQpCnByaW50KHApICAgICAjIFBsb3QgMSAtLT4gaW4gdGhlIGZpcnN0IHBhZ2Ugb2YgUERGCmRldi5vZmYoKQoKcGF0aC5maWd1cmVzICA9ICcvVm9sdW1lcy9TYW1zdW5nX1Q1L3ZpZW5uL3dvcmtfdGUvJwpwZGYocGFzdGUocGF0aC5maWd1cmVzLCAnZ3JhcGhfbW9iX25vdGVfZWR0YV9ub19sZWdlbmQucGRmJywgc2VwID0gJycpLCB3aWR0aCA9IDUsIGhlaWdodCA9IDUpCnByaW50KHArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikpICAgICAjIFBsb3QgMSAtLT4gaW4gdGhlIGZpcnN0IHBhZ2Ugb2YgUERGCmRldi5vZmYoKQoKYGBgCgoKIyMgUGxvdCB3aXRoIGNvbXBvbmVudCBJRApgYGB7cn0KCgp0bXAuZ3JhcGggPC0gaWdyYXBoOjptYWtlX2dyYXBoKHQoc3YuZWRnZXMpLCBkaXJlY3RlZCA9IFQpCnRtcC5ncmFwaCA8LSBpZ3JhcGg6OnNpbXBsaWZ5KHRtcC5ncmFwaCkKdG1wLmNvbXAgPC0gaWdyYXBoOjpjb21wb25lbnRzKHRtcC5ncmFwaCkKCnNpemUubGltaXQgPSA1CmNvbXAuaWQgPSBhcy5jaGFyYWN0ZXIodG1wLmNvbXAkbWVtYmVyc2hpcCkKbmFtZXMoY29tcC5pZCkgPSBuYW1lcyh0bXAuY29tcCRtZW1iZXJzaGlwKQpjb21wLmlkW3RtcC5jb21wJGNzaXplW3RtcC5jb21wJG1lbWJlcnNoaXBdIDwgc2l6ZS5saW1pdF0gPSAnJwoKbmFtZXMudGUgPSBuYW1lcyhnLm5vZGVzLnByb3QpW2cubm9kZXMucHJvdCAlaW4lIGMoJ3RyYW5zcG9zJywgJ3JldmVyc2UnKV0KCmNvbXAuaWRbIShuYW1lcyhjb21wLmlkKSAlaW4lIG5hbWVzLnRlKV0gPSAnJwoKY29tcC5pZFtkdXBsaWNhdGVkKGNvbXAuaWQpXSA9ICcnCgoKY29tcC5yZW1haW4gPSBhcy5udW1lcmljKGNvbXAuaWRbY29tcC5pZCAhPSAnJ10pCmFscGhhID0gcmVwKDAsIGxlbmd0aChiLmdyYXBoLm5hbWVzKSkKbmFtZXMoYWxwaGEpID0gbmFtZXModG1wLmNvbXAkbWVtYmVyc2hpcCkKYWxwaGFbdG1wLmNvbXAkbWVtYmVyc2hpcCAlaW4lIGNvbXAucmVtYWluXSA9IDEKCnNldC5zZWVkKDIzOSkKcCA8LSBnZ25ldDIoZy5wYXJ0LCBsYWJlbCA9IGNvbXAuaWRbYi5ncmFwaC5uYW1lc10sIAogICAgICAgICAgICBsYWJlbC5jb2xvciA9ICJibGFjayIsCiAgICAgICAgICAgIGxhYmVsLnNpemUgPSAzLAogICAgICAgICAgICBlZGdlLmNvbG9yID0gImdyZXkiLCAKICAgICAgICAgICAgYWxwaGEgPSBhbHBoYVtiLmdyYXBoLm5hbWVzXSwKICAgICAgICAgICAgIyBub2RlLnNpemUgPSBnLm5vZGVzLmNudFtiLmdyYXBoLm5hbWVzXSwgCiAgICAgICAgICAgIG5vZGUuc2l6ZSA9IDEsCiAgICAgICAgICAgIGNvbG9yID0gZy5ub2Rlcy5wcm90W2IuZ3JhcGgubmFtZXNdLAogICAgICAgICAgICBwYWxldHRlID0gZy5jb2xzLAogICAgICAgICAgICAjIG1vZGUgPSAia2FtYWRha2F3YWkiCiAgICAgICAgICAgICkgKyBndWlkZXMoc2l6ZSA9IEYpCnAgCgoKcGF0aC5maWd1cmVzICA9ICcvVm9sdW1lcy9TYW1zdW5nX1Q1L3ZpZW5uL3dvcmtfdGUvJwpwZGYocGFzdGUocGF0aC5maWd1cmVzLCAnZ3JhcGhfc3Zfbm90ZV9udW1iZXJzLnBkZicsIHNlcCA9ICcnKSwgd2lkdGggPSA1LCBoZWlnaHQgPSA1KQpwcmludChwICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSkgICAgICMgUGxvdCAxIC0tPiBpbiB0aGUgZmlyc3QgcGFnZSBvZiBQREYKZGV2Lm9mZigpCgoKCiMgT3JkZXIgb2YgY29tcG9uZW50cwpjbnQgPSB0YWJsZSh0bXAuY29tcCRtZW1iZXJzaGlwW3RtcC5jb21wJG1lbWJlcnNoaXAgJWluJSBjb21wLnJlbWFpbl0pCmNudCA9IGNudFtvcmRlcigtY250KV0KCmBgYAoKIyMgQ05WCmBgYHtyfQoKY252ID0gcmVhZFJEUygnL1ZvbHVtZXMvU2Ftc3VuZ19UNS92aWVubi93b3JrX3N2L3NpbWlsYXJfY252X3N2X29uX2FjY2Vzc2lvbnNfY3VtXzAuOS5yZHMnKQoKYGBgCgojIyBQbG90IG9uZSBzcGVjaWZpYyBuZXR3b3JrCmBgYHtyfQoKcGF0aC5maWd1cmVzLmV4YW1wbGVzICA9ICcvVm9sdW1lcy9TYW1zdW5nX1Q1L3ZpZW5uL3dvcmtfdGUvZXhhbXBsZXMvJwoKIyAKIyB0bXAuZ3JhcGggPC0gaWdyYXBoOjptYWtlX2dyYXBoKHQoc3YuZWRnZXMpLCBkaXJlY3RlZCA9IFQpCiMgdG1wLmdyYXBoIDwtIGlncmFwaDo6c2ltcGxpZnkodG1wLmdyYXBoKQojIHRtcC5jb21wIDwtIGlncmFwaDo6Y29tcG9uZW50cyh0bXAuZ3JhcGgpCiMgCiMgdG1wLmNudCA9IHRhYmxlKHRtcC5jb21wJG1lbWJlcnNoaXApCiMgdG1wLmNudCA9IC1zb3J0KC10bXAuY250KQoKdG1wLmNudCA9IGNudAoKZm9yKGsgaW4gMTpsZW5ndGgodG1wLmNudCkpewogIHRtcC5rID0gYXMubnVtZXJpYyhuYW1lcyh0bXAuY250KVtrXSkKICB0bXAubmFtZXMgPSBuYW1lcyh0bXAuY29tcCRtZW1iZXJzaGlwKVt0bXAuY29tcCRtZW1iZXJzaGlwID09IHRtcC5rXQogIGIuZ3JhcGguc3ViID0gc3YuZWRnZXNbKHN2LmVkZ2VzWywxXSAlaW4lIHRtcC5uYW1lcykgJiAKICAgICAgICAgICAgICAgICAgICAgICAgICAoc3YuZWRnZXNbLDJdICVpbiUgdG1wLm5hbWVzKSxdCiAgCiAgCiAgZy5wYXJ0LnN1YiA8LSBuZXR3b3JrKGIuZ3JhcGguc3ViLCBtYXRyaXgudHlwZSA9ICJlZGdlbGlzdCIsIGlnbm9yZS5ldmFsID0gRkFMU0UsIGRpcmVjdGVkID0gVFJVRSkKICBiLmdyYXBoLm5hbWVzLnN1YiA9IG5ldHdvcmsudmVydGV4Lm5hbWVzKGcucGFydC5zdWIpCiAgCiAgCiAgICAKICBiLmdyYXBoLnNpemUuc3ViIDwtIGFzLm51bWVyaWMoc3ViKCIuKlxcfCIsICIiLCBiLmdyYXBoLm5hbWVzLnN1YikpCiAgbmFtZXMoYi5ncmFwaC5zaXplLnN1YikgPSBiLmdyYXBoLm5hbWVzLnN1YgogICMgYi5ncmFwaC5zaXplLnN1YiA9IGNlaWxpbmcobG9nKGIuZ3JhcGguc2l6ZS5zdWIsIDEwKSkKICAKICBpZigobGVuZ3RoKHVuaXF1ZSggZy5ub2Rlcy5wcm90W2IuZ3JhcGgubmFtZXMuc3ViXSkpID09IDEpKXsKICAgIHNldC5zZWVkKDIwKQogICAgcCA8LSBnZ25ldDIoZy5wYXJ0LnN1YiwgbGFiZWwgPSBiLmdyYXBoLnNpemUuc3ViW2IuZ3JhcGgubmFtZXMuc3ViXSwgZWRnZS5jb2xvciA9ICJibGFjayIsIAogICAgICAgICAgICAgICAgbm9kZS5zaXplID0gMTUsCiAgICAgICAgICAgICAgICBhcnJvdy5nYXAgPSAwLjA3LCBhcnJvdy5zaXplID0gMywKICAgICAgICAgICAgICAgIGNvbG9yID0gZy5jb2xzW2cubm9kZXMucHJvdFtiLmdyYXBoLm5hbWVzLnN1Yl1bMV1dLAogICAgICAgICAgICAgICAgKSArIGd1aWRlcyhzaXplID0gRikgKyAgZ2d0aXRsZShwYXN0ZSgnQ29tcG9uZW50ICMnLCB0bXAuaykpCiAgICBwCiAgfSBlbHNlIHsKICAgIHNldC5zZWVkKDIwKQogICAgcCA8LSBnZ25ldDIoZy5wYXJ0LnN1YiwgbGFiZWwgPSBiLmdyYXBoLnNpemUuc3ViW2IuZ3JhcGgubmFtZXMuc3ViXSwgZWRnZS5jb2xvciA9ICJibGFjayIsIAogICAgICAgICAgICAgICAgbm9kZS5zaXplID0gMTUsCiAgICAgICAgICAgICAgICBhcnJvdy5nYXAgPSAwLjA3LCBhcnJvdy5zaXplID0gMywKICAgICAgICAgICAgICAgIGNvbG9yID0gZy5ub2Rlcy5wcm90W2IuZ3JhcGgubmFtZXMuc3ViXSwKICAgICAgICAgICAgICAgIHBhbGV0dGUgPSBnLmNvbHMsCiAgICAgICAgICAgICAgICApICsgZ3VpZGVzKHNpemUgPSBGKSArICBnZ3RpdGxlKHBhc3RlKCdDb21wb25lbnQgIycsIHRtcC5rKSkKICAgIHAKICB9CiAgCiAKICAKICBwZGYocGFzdGUocGF0aC5maWd1cmVzLmV4YW1wbGVzLCAnZ3JhcGhfc3ZfZXhhbXBsZV8nLGssJ19jb21wXycsdG1wLmssJy5wZGYnLCBzZXAgPSAnJyksIHdpZHRoID0gNSwgaGVpZ2h0ID0gNCkKICBwcmludChwICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSkgICAgICMgUGxvdCAxIC0tPiBpbiB0aGUgZmlyc3QgcGFnZSBvZiBQREYKICBkZXYub2ZmKCkKICAKICAjIGFubm90YXRpb24KICBhbm5vdC50bXAgPSBzdi5wcm90W3N2LnByb3QkbmFtZSAlaW4lIGIuZ3JhcGgubmFtZXMuc3ViLF0KICAjIGFubm90LnRtcCA9IGFubm90LnRtcFthbm5vdC50bXAkdHJhbnNwb3MgPT0gMSxdCiAgCiAgd3JpdGUudGFibGUoYW5ub3QudG1wLCBwYXN0ZShwYXRoLmZpZ3VyZXMuZXhhbXBsZXMsICdncmFwaF9zdl9leGFtcGxlXycsaywnX3BibGFzdC50eHQnLCBzZXAgPSAnJyksIAogICAgICAgICAgICAgIHJvdy5uYW1lcyA9IEYsIGNvbC5uYW1lcyA9IEYsIHF1b3RlID0gRiwgc2VwID0gJ1x0JykKICAKICAKICAjIGlmIEVEVEEgYW5ub3RhdGlvbiBleGlzdHMKICBzdi50bXAgPSB1bmlxdWUoYyhiLmdyYXBoLnN1YikpCiAgc3YudG1wLmN1dCA8LSBnc3ViKCJcXHwuKiIsICIiLCBzdi50bXApCiAgc3YuYW5ub3QudG1wID0gc3YuYW5ub3Rbc3YudG1wLmN1dCxdCiAgbi5maXggPSA5CiAgc3YuYW5ub3QudG1wICA9IHN2LmFubm90LnRtcFssYygxOm4uZml4LG4uZml4K3doaWNoKGNvbFN1bXMoc3YuYW5ub3QudG1wWywobi5maXgrMSk6bmNvbChzdi5hbm5vdC50bXApXSkgIT0gMCkpXQogIHJvd25hbWVzKHN2LmFubm90LnRtcCkgPSBzdi50bXAKICAgIAogIHdyaXRlLnRhYmxlKHN2LmFubm90LnRtcCwgcGFzdGUocGF0aC5maWd1cmVzLmV4YW1wbGVzLCAnZ3JhcGhfc3ZfZXhhbXBsZV8nLGssJ19lZHRhLnR4dCcsIHNlcCA9ICcnKSwgCiAgICAgICAgICAgICByb3cubmFtZXMgPSBGLCBxdW90ZSA9IEYsIHNlcCA9ICdcdCcpCiAgCiAgIyBDb3B5ME51bWJlciB2YXJpYXRpb24KICBjbnYudG1wID0gY252W3N2LnRtcCxdCiAgCiAgaGVhdG1hcChjbnYudG1wLCBjb2wgPSBjb2xvclJhbXBQYWxldHRlKGMoIndoaXRlIiwgInJlZCIpKSgyMCkpCiAgCn0KCmBgYAojIFBpZS1jaGFydCBvZiBwcm90ZWlucwpgYGB7cn0KbGlicmFyeShnZ3Bsb3QyKQoKZGF0YSA8LSBkYXRhLmZyYW1lKAogIHR5cGUgPSBjKCJubyBwcm90ZWlucyIsICJURS1yZWxhdGVkIiwgItCa0LDRgtC10LPQvtGA0LjRjyAyIiwgItCa0LDRgtC10LPQvtGA0LjRjyAzIiwgItCa0LDRgtC10LPQvtGA0LjRjyA0IiksCiAgdmFsdWUgPSBjKDEzNSwgNjMsIDg1LCAxMzMpCikKCnBpZS5jaGFydCA8LSBnZ3Bsb3QoZGF0YSwgYWVzKHggPSAiIiwgeSA9IHZhbHVlLCBmaWxsID0gdHlwZSkpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5Iiwgd2lkdGggPSAxKSArCiAgY29vcmRfcG9sYXIoInkiLCBzdGFydCA9IDApICsKICB0aGVtZV92b2lkKCkKCnBpZS5jaGFydAoKYGBgCgojIyBBZG1peHR1cmUgZ3JvdXBzCmBgYHtyfQpncm91cHMgPC0gYygKICAiZ2VybWFueSIsCiAgInNvdXRoX3N3ZWRlbiIsCiAgIm5vcnRoX3N3ZWRlbiIsCiAgInNvdXRoX3N3ZWRlbiIsCiAgIm5vcnRoX3N3ZWRlbiIsCiAgImdlcm1hbnkiLAogICJ3ZXN0ZXJuX2V1cm9wZSIsCiAgImNlbnRyYWxfZXVyb3BlIiwKICAiaXRhbHlfYmFsa2FuX2NhdWNhc3VzIiwKICAic3BhaW4iLAogICJyZWxpY3QiLAogICJhc2lhIiwKICAiY2VudHJhbF9ldXJvcGUiLAogICJhZG1peGVkIiwKICAic3BhaW4iLAogICJyZWxpY3QiLAogICJpdGFseV9iYWxrYW5fY2F1Y2FzdXMiLAogICJ3ZXN0ZXJuX2V1cm9wZSIsCiAgImFzaWEiLAogICJhZnJpY2EiLAogICJjaGluYSIsCiAgImNoaW5hIiwKICAiYWZyaWNhIiwKICAiYWZyaWNhIiwKICAibWFkZWlyYSIsCiAgIm1hZGVpcmEiLAogICJhZnJpY2EiCikKCiMg0JjRgdC/0L7Qu9GM0LfRg9C10Lwg0YTRg9C90LrRhtC40Y4gdGFibGUoKSDQtNC70Y8g0L/QvtC00YHRh9C10YLQsCDQutC+0LvQuNGH0LXRgdGC0LLQsCDRjdC70LXQvNC10L3RgtC+0LIg0LIg0LrQsNC20LTQvtC5INCz0YDRg9C/0L/QtQphcy5tYXRyaXgodGFibGUoZ3JvdXBzKSkKYGBgCgoKCiMgT0xECmBgYHtyfQpzdW5zZXQgPC0gY29sb3VyKCJzdW5zZXQiKQpkaXNjcmV0ZV9yYWluYm93IDwtIGNvbG91cigiZGlzY3JldGUgcmFpbmJvdyIpCgpmaWxlLnRlID0gJy9Wb2x1bWVzL1NhbXN1bmdfVDUvdmllbm4vd29yay9ibGFzdF90ZXNfYW5uLnR4dCcKc2ltLmN1dG9mZiA9IDAuODUKbGVuLmN1dG9mZiA9IDEwMApgYGAKCgpgYGB7cn0KCmIgPSByZWFkLnRhYmxlKGZpbGUudGUsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpiID0gYltiJFYxICE9IGIkVjgsXQpiJGxlbjEgPSBhcy5udW1lcmljKHNhcHBseShiJFYxLCBmdW5jdGlvbihzKSBzdHJzcGxpdChzLCAnXFx8JylbWzFdXVs3XSkpCmIkbGVuMiA9IGFzLm51bWVyaWMoc2FwcGx5KGIkVjgsIGZ1bmN0aW9uKHMpIHN0cnNwbGl0KHMsICdcXHwnKVtbMV1dWzddKSkKYiA9IGJbYiRsZW4xID49IGxlbi5jdXRvZmYsXQpiID0gYltiJGxlbjIgPj0gbGVuLmN1dG9mZixdCmIkY29tYiA9IHBhc3RlKGIkVjEsIGIkVjgsIHNlcCA9ICdeJykKCiMgT3JkZXIgcG9zaXRpb25zIGluIGJhc2UKaWR4ID0gYiRWNCA+IGIkVjUKdG1wID0gYltpZHgsICdWNCddCmJbaWR4LCAnVjQnXSA9IGJbaWR4LCAnVjUnXQpiW2lkeCwgJ1Y1J10gPSB0bXAKCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBHZXQgc2VwYXJhdGVseSB0aG9zZSwgd2hvIGhhcyBhIHVuaXF1ZSBjb3ZlcmFnZQpjb21iLnRibCA9IHRhYmxlKGIkY29tYikKaWR4LnVuaSA9IGIkY29tYiAlaW4lIG5hbWVzKGNvbWIudGJsKVtjb21iLnRibCA9PSAxXQpiLnVuaSA9IGJbaWR4LnVuaSxdCmIgPSBiWyFpZHgudW5pLF0KCiMgVGhpcyB2YXJpYWJsZSB3aWxsIGJlIHVzZWQgbGF0ZXIKYi51bmkkcDEgPSAoYi51bmkkVjMgLSBiLnVuaSRWMiArIDEpIC8gYi51bmkkbGVuMQpiLnVuaSRwMiA9IChiLnVuaSRWNSAtIGIudW5pJFY0ICsgMSkgLyBiLnVuaSRsZW4yCmIudW5pID0gYi51bmlbKGIudW5pJHAxID49IHNpbS5jdXRvZmYpIHwgKGIudW5pJHAyID49IHNpbS5jdXRvZmYpLF0KCmIucmVsYXRpb25zID0gZGF0YS5mcmFtZShzdWIudGUgPSBiLnVuaSRWMVtiLnVuaSRwMSA+PSBzaW0uY3V0b2ZmXSwKICAgICAgICAgICAgICAgICAgICAgICAgIHRlID0gYi51bmkkVjhbYi51bmkkcDEgPj0gc2ltLmN1dG9mZl0sIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpiLnJlbGF0aW9ucyA9IHJiaW5kKGIucmVsYXRpb25zLAogICAgICAgICAgICAgICAgICAgIGRhdGEuZnJhbWUoc3ViLnRlID0gYi51bmkkVjhbYi51bmkkcDIgPj0gc2ltLmN1dG9mZl0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0ZSA9IGIudW5pJFYxW2IudW5pJHAyID49IHNpbS5jdXRvZmZdLCBzdHJpbmdzQXNGYWN0b3JzID0gRikpCmIucmVsYXRpb25zID0gdW5pcXVlKGIucmVsYXRpb25zKQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIE1pbi1tYXggb2YgdGhlIGNvdmVyYWdlIHRvIHJlbW92ZSB0aG9zZSwgd2hvIGFyZSBOT1QgaW4gZWFjaCBvdGhlciBjb21wbGV0ZWx5CmIuY292ID0gdGFwcGx5KGIkVjIsIGIkY29tYiwgbWluKQpiLmNvdiA9IGRhdGEuZnJhbWUoY29tYiA9IG5hbWVzKGIuY292KSwgVjIgPSBiLmNvdikKYi5jb3YkVjMgPSB0YXBwbHkoYiRWMywgYiRjb21iLCBtYXgpCmIuY292JFY0ID0gdGFwcGx5KGIkVjQsIGIkY29tYiwgbWluKQpiLmNvdiRWNSA9IHRhcHBseShiJFY1LCBiJGNvbWIsIG1heCkKYi5jb3YkbGVuMSA9IHRhcHBseShiJGxlbjEsIGIkY29tYiwgdW5pcXVlKQpiLmNvdiRsZW4yID0gdGFwcGx5KGIkbGVuMiwgYiRjb21iLCB1bmlxdWUpCmIuY292JHAxID0gKGIuY292JFYzIC0gYi5jb3YkVjIgKyAxKSAvIGIuY292JGxlbjEKYi5jb3YkcDIgPSAoYi5jb3YkVjUgLSBiLmNvdiRWNCArIDEpIC8gYi5jb3YkbGVuMgoKY29tYi51bmNvdiA9IGIuY292JGNvbWJbKGIuY292JHAxIDwgc2ltLmN1dG9mZikgJiAoYi5jb3YkcDIgPCBzaW0uY3V0b2ZmKV0KCmIgPSBiWyEoYiRjb21iICVpbiUgY29tYi51bmNvdiksXQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIENhbGN1bGF0ZSB0aGUgY292ZXJhZ2UgZGlyZWN0bHkgZm9yIHRoZSBmaXJzdApiID0gYltvcmRlcihiJFYzKSxdCmIgPSBiW29yZGVyKGIkVjIpLF0KYiA9IGJbb3JkZXIoYiRjb21iKSxdCgojIFJlbW92ZSBuZXN0ZWQKaWR4ID0gd2hpY2goKGIkVjNbLW5yb3coYildID4gYiRWM1stMV0pICYgKGIkY29tYlstbnJvdyhiKV0gPT0gYiRjb21iWy0xXSkpICsgMQpiMSA9IGJbLWlkeCxdCgojIENvbXB1dGUgZ2FwcwpiMSRnYXAgPSBjKGIxJFYyWy0xXSAtIGIxJFYzWy1ucm93KGIxKV0gLSAxLCAwKQpiMSRnYXBbYjEkZ2FwIDwgMF0gPSAwCmlkeC5kaWZmLmNvbWIgPSB3aGljaChiMSRjb21iWy0xXSAhPSBiMSRjb21iWy1ucm93KGIxKV0pCmIxJGdhcFtpZHguZGlmZi5jb21iXSA9IDAKCmIuY292ID0gdGFwcGx5KGIxJFYyLCBiMSRjb21iLCBtaW4pCmIuY292ID0gZGF0YS5mcmFtZShjb21iID0gbmFtZXMoYi5jb3YpLCBWMiA9IGIuY292KQpiLmNvdiRWMyA9IHRhcHBseShiMSRWMywgYjEkY29tYiwgbWF4KQpiLmNvdiRsZW4xID0gdGFwcGx5KGIxJGxlbjEsIGIxJGNvbWIsIHVuaXF1ZSkKYi5jb3YkZ2FwID0gdGFwcGx5KGIxJGdhcCwgYjEkY29tYiwgc3VtKQpiLmNvdiRsZW4xID0gYi5jb3YkbGVuMSAKYi5jb3YkcDEgPSAoYi5jb3YkVjMgLSBiLmNvdiRWMiArIDEgLSBiLmNvdiRnYXApIC8gYi5jb3YkbGVuMQpiLmNvdiRWMSA9IHRhcHBseShiMSRWMSwgYjEkY29tYiwgdW5pcXVlKQpiLmNvdiRWOCA9IHRhcHBseShiMSRWOCwgYjEkY29tYiwgdW5pcXVlKQoKYi5jb3YgPSBiLmNvdltiLmNvdiRwMSA+PSBzaW0uY3V0b2ZmLF0KCgpiLnJlbGF0aW9ucyA9IHJiaW5kKGIucmVsYXRpb25zLAogICAgICAgICAgICAgICAgICAgIGRhdGEuZnJhbWUoc3ViLnRlID0gYi5jb3YkVjEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0ZSA9IGIuY292JFY4LCBzdHJpbmdzQXNGYWN0b3JzID0gRikpCgoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIENhbGN1bGF0ZSB0aGUgY292ZXJhZ2UgZGlyZWN0bHkgZm9yIHRoZSBzZWNvbmQKYiA9IGJbb3JkZXIoYiRWNSksXQpiID0gYltvcmRlcihiJFY0KSxdCmIgPSBiW29yZGVyKGIkY29tYiksXQoKIyBSZW1vdmUgbmVzdGVkCmlkeCA9IHdoaWNoKChiJFY1Wy1ucm93KGIpXSA+IGIkVjVbLTFdKSAmIChiJGNvbWJbLW5yb3coYildID09IGIkY29tYlstMV0pKSArIDEKYjEgPSBiWy1pZHgsXQoKIyBDb21wdXRlIGdhcHMKYjEkZ2FwID0gYyhiMSRWNFstMV0gLSBiMSRWNVstbnJvdyhiMSldIC0gMSwgMCkKYjEkZ2FwW2IxJGdhcCA8IDBdID0gMAppZHguZGlmZi5jb21iID0gd2hpY2goYjEkY29tYlstMV0gIT0gYjEkY29tYlstbnJvdyhiMSldKQpiMSRnYXBbaWR4LmRpZmYuY29tYl0gPSAwCgpiLmNvdiA9IHRhcHBseShiMSRWNCwgYjEkY29tYiwgbWluKQpiLmNvdiA9IGRhdGEuZnJhbWUoY29tYiA9IG5hbWVzKGIuY292KSwgVjQgPSBiLmNvdikKYi5jb3YkVjUgPSB0YXBwbHkoYjEkVjUsIGIxJGNvbWIsIG1heCkKYi5jb3YkbGVuMiA9IHRhcHBseShiMSRsZW4yLCBiMSRjb21iLCB1bmlxdWUpCmIuY292JGdhcCA9IHRhcHBseShiMSRnYXAsIGIxJGNvbWIsIHN1bSkKYi5jb3YkbGVuMiA9IGIuY292JGxlbjIgCmIuY292JHAxID0gKGIuY292JFY1IC0gYi5jb3YkVjQgKyAxIC0gYi5jb3YkZ2FwKSAvIGIuY292JGxlbjIKYi5jb3YkVjEgPSB0YXBwbHkoYjEkVjEsIGIxJGNvbWIsIHVuaXF1ZSkKYi5jb3YkVjggPSB0YXBwbHkoYjEkVjgsIGIxJGNvbWIsIHVuaXF1ZSkKCmIuY292ID0gYi5jb3ZbYi5jb3YkcDEgPj0gc2ltLmN1dG9mZixdCgoKYi5yZWxhdGlvbnMgPSByYmluZChiLnJlbGF0aW9ucywKICAgICAgICAgICAgICAgICAgICBkYXRhLmZyYW1lKHN1Yi50ZSA9IGIuY292JFY4LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGUgPSBiLmNvdiRWMSwgc3RyaW5nc0FzRmFjdG9ycyA9IEYpKQoKICAKYi5yZWxhdGlvbnMgPSB1bmlxdWUoYi5yZWxhdGlvbnMpCgoKYi5yZWxhdGlvbnMKCmBgYAoKCiMgRGVmaW5lIGNsdXN0ZXJzCmBgYHtyfQpiLm5vZGVzID0gcmJpbmQoYi5yZWxhdGlvbnMsCiAgICAgICAgICAgICAgICAgICAgZGF0YS5mcmFtZShzdWIudGUgPSBiLnJlbGF0aW9ucyR0ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRlID0gYi5yZWxhdGlvbnMkc3ViLnRlKSkKCmIubm9kZXMkY29tYiA9IHBhc3RlKGIubm9kZXMkc3ViLnRlLCBiLm5vZGVzJHRlLCBzZXAgPSAnXicpCgpjb21iLnRibCA9IHRhYmxlKGIubm9kZXMkY29tYikKY29tYi5iYWNrLmFuZC5mb3RoID0gbmFtZXMoY29tYi50YmwpW2NvbWIudGJsID49IDJdCmIubm9kZXMgPSBiLm5vZGVzW2Iubm9kZXMkY29tYiAlaW4lIGNvbWIuYmFjay5hbmQuZm90aCxdCmIubm9kZXMgPSB1bmlxdWUoYi5ub2Rlc1ssIGMoJ3N1Yi50ZScsICd0ZScpXSkKCgp0ZS5ub2RlcyA8LSBpZ3JhcGg6Om1ha2VfZ3JhcGgodChiLm5vZGVzKSwgZGlyZWN0ZWQgPSBUKQp0ZS5ub2RlcyA8LSBpZ3JhcGg6OnNpbXBsaWZ5KHRlLm5vZGVzKQp0ZS5ub2Rlcy5jb21wIDwtIGlncmFwaDo6Y29tcG9uZW50cyh0ZS5ub2RlcykKCm5vZGVzID0gcGFzdGUoJ04nLCB0ZS5ub2Rlcy5jb21wJG1lbWJlcnNoaXAsIHNlcCA9ICcnKQpuYW1lcyhub2RlcykgPSBuYW1lcyh0ZS5ub2Rlcy5jb21wJG1lbWJlcnNoaXApCmBgYAoKIyMgSWRlbnRpZnkgZmFtaWx5IGZvciBlYWNoIG5vZGUKYGBge3J9Cgpub2Rlcy5mYW1pbHkgPSBzYXBwbHkobmFtZXMobm9kZXMpLCBmdW5jdGlvbihzKSBzdHJzcGxpdChzLCAnXFx8JylbWzFdXVs2XSkKCm5vZGVzLmZhbWlseS5tYXggPSB0YXBwbHkobm9kZXMuZmFtaWx5LCBub2RlcywgZnVuY3Rpb24ocyl7CiAgdGJsID0gdGFibGUocykKICBmID0gbmFtZXModGJsKVt0YmwgPT0gbWF4KHRibCldCiAgaWYobGVuZ3RoKGYpID09IDEpewogICAgcmV0dXJuKGYpCiAgfSBlbHNlIHsKICAgIHJldHVybignTWl4JykKICB9Cn0pCgpub2Rlcy5mYW1pbHkubWF4W25vZGVzLmZhbWlseS5tYXggJWluJSBjKCdETkEvUG9nbycsICdETkEvVGMxJywgJ0ROQS9IYXJiaW5nZXInLCAnRE5BL0VuLVNwbScsCiAgICAgICAgICAgICAgICAgICAgICdETkEvSEFUJywgJ0ROQScsICdETkEvTWFyaW5lcicpXSA9ICdETkEnCm5vZGVzLmZhbWlseS5tYXhbbm9kZXMuZmFtaWx5Lm1heCAlaW4lIGMoJ1JhdGhFMV9jb25zJywgJ1JhdGhFMl9jb25zJyldID0gJ0ROQScKbm9kZXMuZmFtaWx5Lm1heFtub2Rlcy5mYW1pbHkubWF4ICVpbiUgYygnTElORS9MMScsICdMSU5FPycpXSA9ICdMSU5FJwpub2Rlcy5mYW1pbHkubWF4W25vZGVzLmZhbWlseS5tYXggJWluJSBjKCdVbmFzc2lnbmVkJyldID0gJ01peCcKbm9kZXMuZmFtaWx5LnVuaXF1ZSA9IHVuaXF1ZShub2Rlcy5mYW1pbHkubWF4KQoKCgpgYGAKCgojIyBHcmFwaCB3aXRob3V0IHNpbmdsZXRvbnMKYGBge3J9CgpiLmdyYXBoLmluaXQgPSBiLnJlbGF0aW9uc1soYi5yZWxhdGlvbnMkc3ViLnRlICVpbiUgbmFtZXMobm9kZXMpKSAmIChiLnJlbGF0aW9ucyR0ZSAlaW4lIG5hbWVzKG5vZGVzKSksXQpiLmdyYXBoID0gYi5ncmFwaC5pbml0CmIuZ3JhcGggPSBjYmluZChub2Rlc1thcy5jaGFyYWN0ZXIoYi5ncmFwaCRzdWIudGUpXSwgbm9kZXNbYXMuY2hhcmFjdGVyKGIuZ3JhcGgkdGUpXSkKYi5ncmFwaCA9IHVuaXF1ZShiLmdyYXBoKQoKCmIuZ3JhcGggPSBiLmdyYXBoW2IuZ3JhcGhbLDFdICE9IGIuZ3JhcGhbLDJdLF0KCiMgcmVkdWNlIGluZGlyZWN0IGFycm93cwppZHgucmVtb3ZlID0gYygpCmZvcihpLmVkZ2UgaW4gMTpucm93KGIuZ3JhcGgpKXsKICBpZihpLmVkZ2UgJSUgMTAwMCA9PSAwKSBwcmludChpLmVkZ2UpCiAgdG1wLnRvID0gYi5ncmFwaFtiLmdyYXBoWywxXSA9PSBiLmdyYXBoW2kuZWRnZSwxXSwyXQogIHRtcC5mcm9tID0gYi5ncmFwaFtiLmdyYXBoWywyXSA9PSBiLmdyYXBoW2kuZWRnZSwyXSwxXQogIGlmKGxlbmd0aChpbnRlcnNlY3QodG1wLnRvLCB0bXAuZnJvbSkpID4gMCkgaWR4LnJlbW92ZSA9IGMoaWR4LnJlbW92ZSwgaS5lZGdlKQp9CmlkeC5yZW1vdmUgPSB1bmlxdWUoaWR4LnJlbW92ZSkKYi5ncmFwaCA9IGIuZ3JhcGhbLWlkeC5yZW1vdmUsXQoKCiMgdGUuZ3JhcGggPC0gaWdyYXBoOjptYWtlX2dyYXBoKHQoYi5ncmFwaCksIGRpcmVjdGVkID0gVCkKIyB0ZS5ncmFwaCA8LSBpZ3JhcGg6OnNpbXBsaWZ5KHRlLmdyYXBoKQojIHRlLmdyYXBoLmNvbXAgPC0gaWdyYXBoOjpjb21wb25lbnRzKHRlLmdyYXBoKQoKCm5vZGVzLmZhbWlseS5tYXguZ3JhcGggPSBub2Rlcy5mYW1pbHkubWF4W25hbWVzKG5vZGVzLmZhbWlseS5tYXgpICVpbiUgdW5pcXVlKGMoYi5ncmFwaFssMV0sIGIuZ3JhcGhbLDJdKSldCgpncmFwaC5jb2xzID0gc3Vuc2V0KGxlbmd0aCh1bmlxdWUobm9kZXMuZmFtaWx5Lm1heC5ncmFwaCkpKQoKZ3JhcGguY29scyA9IGRpc2NyZXRlX3JhaW5ib3cobGVuZ3RoKHVuaXF1ZShub2Rlcy5mYW1pbHkubWF4LmdyYXBoKSkpCm5hbWVzKGdyYXBoLmNvbHMpID0gdW5pcXVlKG5vZGVzLmZhbWlseS5tYXguZ3JhcGgpCmcucGFydCA8LSBuZXR3b3JrKGIuZ3JhcGgsIG1hdHJpeC50eXBlID0gImVkZ2VsaXN0IiwgaWdub3JlLmV2YWwgPSBGQUxTRSwgZGlyZWN0ZWQgPSBUUlVFKQpwIDwtIGdnbmV0MihnLnBhcnQsIGxhYmVsID0gRkFMU0UsIGVkZ2UuY29sb3IgPSAiYmxhY2siLCBub2RlLnNpemUgPSAxLCAKICAgICAgICAgICAgY29sb3IgPSBub2Rlcy5mYW1pbHkubWF4LmdyYXBoLCBwYWxldHRlID0gZ3JhcGguY29scywKICAgICAgICAgICAgbW9kZSA9ICJrYW1hZGFrYXdhaSIpIyArIGd1aWRlcyhzaXplID0gRkFMU0UpCnAKCmBgYAojIyBHcmFwaCBXSVRIIHNpbmdsZXRvbnMKYGBge3J9CgoKbmFtZXMuY29yZSA9IG5hbWVzKG5vZGVzLmZhbWlseS5tYXguZ3JhcGgpCgpiLmdyYXBoLmluaXQgPSBiLnJlbGF0aW9ucwpmb3IoaSBpbiAxOjIpewogIGIuZ3JhcGguaW5pdFtiLmdyYXBoLmluaXRbLGldICVpbiUgbmFtZXMobm9kZXMpLCBpXSA9IG5vZGVzW2IuZ3JhcGguaW5pdFtiLmdyYXBoLmluaXRbLGldICVpbiUgbmFtZXMobm9kZXMpLCBpXV0KfQoKYi5ncmFwaCA9IHVuaXF1ZShiLmdyYXBoLmluaXQpCmIuZ3JhcGggPSBiLmdyYXBoW2IuZ3JhcGhbLDFdICE9IGIuZ3JhcGhbLDJdLF0KYi5ncmFwaCA9IHVuaXF1ZShiLmdyYXBoKQojIFZlcnRlY2VzIGZyb20gdGhlIHByZXZpb3VzIGdyYXBoCmIuZ3JhcGggPSBiLmdyYXBoWyhiLmdyYXBoWywxXSAlaW4lIG5hbWVzLmNvcmUpIHwgKGIuZ3JhcGhbLDJdICVpbiUgbmFtZXMuY29yZSksXQoKCiMgcmVkdWNlIGluZGlyZWN0IGFycm93cwppZHgucmVtb3ZlID0gYygpCmZvcihpLmVkZ2UgaW4gMTpucm93KGIuZ3JhcGgpKXsKICBpZihpLmVkZ2UgJSUgMTAwMCA9PSAwKSBwcmludChpLmVkZ2UpCiAgdG1wLnRvID0gYi5ncmFwaFtiLmdyYXBoWywxXSA9PSBiLmdyYXBoW2kuZWRnZSwxXSwyXQogIHRtcC5mcm9tID0gYi5ncmFwaFtiLmdyYXBoWywyXSA9PSBiLmdyYXBoW2kuZWRnZSwyXSwxXQogIGlmKGxlbmd0aChpbnRlcnNlY3QodG1wLnRvLCB0bXAuZnJvbSkpID4gMCkgaWR4LnJlbW92ZSA9IGMoaWR4LnJlbW92ZSwgaS5lZGdlKQp9CmlkeC5yZW1vdmUgPSB1bmlxdWUoaWR4LnJlbW92ZSkKYi5ncmFwaCA9IGIuZ3JhcGhbLWlkeC5yZW1vdmUsXQoKdGUuZ3JhcGggPC0gaWdyYXBoOjptYWtlX2dyYXBoKHQoYi5ncmFwaCksIGRpcmVjdGVkID0gVCkKZCA8LSBpZ3JhcGg6OmRpc3RhbmNlcyh0ZS5ncmFwaCkKIyB0ZS5ncmFwaCA8LSBpZ3JhcGg6OnNpbXBsaWZ5KHRlLmdyYXBoKQojIHRlLmdyYXBoLmNvbXAgPC0gaWdyYXBoOjpjb21wb25lbnRzKHRlLmdyYXBoKQoKbmFtZXMubmV3ID0gdW5pcXVlKHNldGRpZmYoYyhiLmdyYXBoWywxXSwgYi5ncmFwaFssMl0pLCBuYW1lcyhub2Rlcy5mYW1pbHkubWF4KSkpCiMgbmFtZXMubmV3LnZhbCA9IHBhc3RlKCdHJywxOmxlbmd0aChuYW1lcy5uZXcpLCBzZXAgPSAnJykKIyBuYW1lcyhuYW1lcy5uZXcudmFsKSA9IG5hbWVzLm5ldwojIG5hbWVzLm5ldy52YWwgPSAKCm5hbWVzLm5ldy5mYW1pbHkgPSBzYXBwbHkobmFtZXMubmV3LCBmdW5jdGlvbihzKSBzdHJzcGxpdChzLCAnXFx8JylbWzFdXVs2XSkKbmFtZXMubmV3LmZhbWlseVtuYW1lcy5uZXcuZmFtaWx5ICVpbiUgYygnRE5BL1BvZ28nLCAnRE5BL1RjMScsICdETkEvSGFyYmluZ2VyJywgJ0ROQS9Fbi1TcG0nLAogICAgICAgICAgICAgICAgICAgICAnRE5BL0hBVCcsICdETkEnLCAnRE5BL01hcmluZXInKV0gPSAnRE5BJwpuYW1lcy5uZXcuZmFtaWx5W25hbWVzLm5ldy5mYW1pbHkgJWluJSBjKCdSYXRoRTFfY29ucycsICdSYXRoRTJfY29ucycpXSA9ICdETkEnCm5hbWVzLm5ldy5mYW1pbHlbbmFtZXMubmV3LmZhbWlseSAlaW4lIGMoJ0xJTkUvTDEnLCAnTElORT8nKV0gPSAnTElORScKbmFtZXMubmV3LmZhbWlseVtuYW1lcy5uZXcuZmFtaWx5ICVpbiUgYygnVW5hc3NpZ25lZCcpXSA9ICdNaXgnCgoKbm9kZXMuZmFtaWx5Lm1heC5hZGQgPSBjKG5vZGVzLmZhbWlseS5tYXgsIG5hbWVzLm5ldy5mYW1pbHkpCm5vZGVzLmZhbWlseS5tYXguYWRkID0gbm9kZXMuZmFtaWx5Lm1heC5hZGRbdW5pcXVlKGMoYi5ncmFwaFssMV0sIGIuZ3JhcGhbLDJdKSldCgpncmFwaC5jb2xzID0gZGlzY3JldGVfcmFpbmJvdyhsZW5ndGgodW5pcXVlKG5vZGVzLmZhbWlseS5tYXguYWRkKSkpCmdyYXBoLmNvbHMgPSBzYW1wbGUoZ3JhcGguY29scykKbmFtZXMoZ3JhcGguY29scykgPSB1bmlxdWUobm9kZXMuZmFtaWx5Lm1heC5hZGQpCgpnLnBhcnQgPC0gbmV0d29yayhiLmdyYXBoLCBtYXRyaXgudHlwZSA9ICJlZGdlbGlzdCIsIGlnbm9yZS5ldmFsID0gRkFMU0UsIGRpcmVjdGVkID0gVFJVRSkKcCA8LSBnZ25ldDIoZy5wYXJ0LCBsYWJlbCA9IEZBTFNFLCBlZGdlLmNvbG9yID0gImJsYWNrIiwgbm9kZS5zaXplID0gMC41LCAKICAgICAgICAgICAgY29sb3IgPSBub2Rlcy5mYW1pbHkubWF4LmFkZCwKICAgICAgICAgICAgcGFsZXR0ZSA9IGdyYXBoLmNvbHMsIG1vZGUgPSAia2FtYWRha2F3YWkiKQpwCmBgYAoKIyBUU05FCmBgYHtyfQoKCmxpYnJhcnkoUnRzbmUpCgoKCgpkIDwtIGlncmFwaDo6ZGlzdGFuY2VzKHRlLmdyYXBoKQpkLm1heCA9IG1heChkWyFpcy5pbmZpbml0ZShkKV0pCgpkW2lzLmluZmluaXRlKGQpXSA9IGQubWF4ICogMS4zCgp0U05FIDwtIFJ0c25lKGQsIGlzX2Rpc3RhbmNlID0gVFJVRSwgZGltcyA9IDIpCgpwbG90KHRTTkUkWVssMV0sIHRTTkUkWVssMl0pCgpgYGAKCgoK